Added:
trunk/user/src/com/google/gwt/rpc/
trunk/user/src/com/google/gwt/rpc/RPC.gwt.xml
trunk/user/src/com/google/gwt/rpc/client/
trunk/user/src/com/google/gwt/rpc/client/RpcService.java
trunk/user/src/com/google/gwt/rpc/client/ast/
trunk/user/src/com/google/gwt/rpc/client/ast/ArrayValueCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/BooleanValueCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/ByteValueCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/CharValueCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/CommandSink.java
trunk/user/src/com/google/gwt/rpc/client/ast/DoubleValueCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/EnumValueCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/FloatValueCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/HasSetters.java
trunk/user/src/com/google/gwt/rpc/client/ast/HasTargetClass.java
trunk/user/src/com/google/gwt/rpc/client/ast/HasValues.java
trunk/user/src/com/google/gwt/rpc/client/ast/IdentityValueCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/InstantiateCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/IntValueCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/InvokeCustomFieldSerializerCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/LongValueCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/NullValueCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/ReturnCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/RpcCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/RpcCommandVisitor.java
trunk/user/src/com/google/gwt/rpc/client/ast/ScalarValueCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/SetCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/ShortValueCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/StringValueCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/ThrowCommand.java
trunk/user/src/com/google/gwt/rpc/client/ast/ValueCommand.java
trunk/user/src/com/google/gwt/rpc/client/impl/
trunk/user/src/com/google/gwt/rpc/client/impl/ClientWriterFactory.java
trunk/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamReader.java
trunk/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamWriter.java
trunk/user/src/com/google/gwt/rpc/client/impl/CommandSerializationStreamWriterBase.java
trunk/user/src/com/google/gwt/rpc/client/impl/CommandToStringWriter.java
trunk/user/src/com/google/gwt/rpc/client/impl/EscapeUtil.java
trunk/user/src/com/google/gwt/rpc/client/impl/HasValuesCommandSink.java
trunk/user/src/com/google/gwt/rpc/client/impl/ListCommandSink.java
trunk/user/src/com/google/gwt/rpc/client/impl/RemoteException.java
trunk/user/src/com/google/gwt/rpc/client/impl/RpcCallbackAdapter.java
trunk/user/src/com/google/gwt/rpc/client/impl/RpcServiceProxy.java
trunk/user/src/com/google/gwt/rpc/client/impl/SimplePayloadSink.java
trunk/user/src/com/google/gwt/rpc/client/impl/TypeOverrides.java
trunk/user/src/com/google/gwt/rpc/linker/
trunk/user/src/com/google/gwt/rpc/linker/ClientOracleLinker.java
trunk/user/src/com/google/gwt/rpc/linker/RpcDataArtifact.java
trunk/user/src/com/google/gwt/rpc/rebind/
trunk/user/src/com/google/gwt/rpc/rebind/RpcProxyCreator.java
trunk/user/src/com/google/gwt/rpc/rebind/RpcServiceGenerator.java
trunk/user/src/com/google/gwt/rpc/server/
trunk/user/src/com/google/gwt/rpc/server/ClientOracle.java
trunk/user/src/com/google/gwt/rpc/server/CommandSerializationUtil.java
trunk/user/src/com/google/gwt/rpc/server/CommandServerSerializationStreamReader.java
trunk/user/src/com/google/gwt/rpc/server/CommandServerSerializationStreamWriter.java
trunk/user/src/com/google/gwt/rpc/server/DelegatingClientOracle.java
trunk/user/src/com/google/gwt/rpc/server/HostedModeClientOracle.java
trunk/user/src/com/google/gwt/rpc/server/Pair.java
trunk/user/src/com/google/gwt/rpc/server/RPC.java
trunk/user/src/com/google/gwt/rpc/server/RpcServlet.java
trunk/user/src/com/google/gwt/rpc/server/SimplePayloadDecoder.java
trunk/user/src/com/google/gwt/rpc/server/WebModeClientOracle.java
trunk/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java
trunk/user/src/com/google/gwt/user/server/rpc/AbstractRemoteServiceServlet.java
trunk/user/src/com/google/gwt/user/server/rpc/HybridServiceServlet.java
trunk/user/super/com/google/gwt/rpc/
trunk/user/super/com/google/gwt/rpc/super/
trunk/user/super/com/google/gwt/rpc/super/com/
trunk/user/super/com/google/gwt/rpc/super/com/google/
trunk/user/super/com/google/gwt/rpc/super/com/google/gwt/
trunk/user/super/com/google/gwt/rpc/super/com/google/gwt/rpc/
trunk/user/super/com/google/gwt/rpc/super/com/google/gwt/rpc/client/
trunk/user/super/com/google/gwt/rpc/super/com/google/gwt/rpc/client/impl/
trunk/user/super/com/google/gwt/rpc/super/com/google/gwt/rpc/client/impl/ClientWriterFactory.java
trunk/user/super/com/google/gwt/rpc/super/com/google/gwt/rpc/client/impl/EscapeUtil.java
trunk/user/test/com/google/gwt/rpc/
trunk/user/test/com/google/gwt/rpc/RPCSuite.gwt.xml
trunk/user/test/com/google/gwt/rpc/client/
trunk/user/test/com/google/gwt/rpc/client/RpcCollectionsTest.java
trunk/user/test/com/google/gwt/rpc/client/RpcCustomFieldSerializerTest.java
trunk/user/test/com/google/gwt/rpc/client/RpcEnumsTest.java
trunk/user/test/com/google/gwt/rpc/client/RpcInheritanceTest.java
trunk/user/test/com/google/gwt/rpc/client/RpcObjectGraphTest.java
trunk/user/test/com/google/gwt/rpc/client/RpcRemoteServiceServletTest.java
trunk/user/test/com/google/gwt/rpc/client/RpcRunTimeSerializationErrorsTest.java
trunk/user/test/com/google/gwt/rpc/client/RpcUnicodeEscapingTest.java
trunk/user/test/com/google/gwt/rpc/client/RpcValueTypesTest.java
Modified:
trunk/samples/dynatable/src/com/google/gwt/sample/dynatable/DynaTable.gwt.xml
trunk/samples/dynatable/src/com/google/gwt/sample/dynatable/client/SchoolCalendarService.java
trunk/samples/dynatable/src/com/google/gwt/sample/dynatable/server/SchoolCalendarServiceImpl.java
trunk/user/src/com/google/gwt/junit/server/JUnitHostImpl.java
trunk/user/src/com/google/gwt/user/client/rpc/RpcRequestBuilder.java
trunk/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java
trunk/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
trunk/user/src/com/google/gwt/user/client/rpc/impl/RemoteServiceProxy.java
trunk/user/src/com/google/gwt/user/rebind/ClassSourceFileComposer.java
trunk/user/src/com/google/gwt/user/rebind/ClassSourceFileComposerFactory.java
trunk/user/src/com/google/gwt/user/rebind/rpc/CustomFieldSerializerValidator.java
trunk/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
trunk/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
trunk/user/src/com/google/gwt/user/rebind/rpc/SerializationUtils.java
trunk/user/src/com/google/gwt/user/rebind/rpc/ServiceInterfaceProxyGenerator.java
trunk/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java
trunk/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
trunk/user/src/com/google/gwt/user/server/rpc/impl/SerializabilityUtil.java
trunk/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
trunk/user/test/com/google/gwt/user/RPCSuite.java
trunk/user/test/com/google/gwt/user/client/rpc/CollectionsTest.java
trunk/user/test/com/google/gwt/user/client/rpc/CollectionsTestService.java
trunk/user/test/com/google/gwt/user/client/rpc/CollectionsTestServiceAsync.java
trunk/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTestSetValidator.java
trunk/user/test/com/google/gwt/user/client/rpc/EnumsTest.java
trunk/user/test/com/google/gwt/user/client/rpc/InheritanceTest.java
trunk/user/test/com/google/gwt/user/client/rpc/TestSetFactory.java
trunk/user/test/com/google/gwt/user/client/rpc/TestSetValidator.java
trunk/user/test/com/google/gwt/user/client/rpc/ValueTypesTest.java
trunk/user/test/com/google/gwt/user/server/rpc/CollectionsTestServiceImpl.java
trunk/user/test/com/google/gwt/user/server/rpc/CustomFieldSerializerTestServiceImpl.java
trunk/user/test/com/google/gwt/user/server/rpc/EnumsTestServiceImpl.java
trunk/user/test/com/google/gwt/user/server/rpc/InheritanceTestServiceImpl.java
trunk/user/test/com/google/gwt/user/server/rpc/MixedSerializableEchoServiceImpl.java
trunk/user/test/com/google/gwt/user/server/rpc/ObjectGraphTestServiceImpl.java
trunk/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTestServiceImplBase.java
trunk/user/test/com/google/gwt/user/server/rpc/UnicodeEscapingServiceImpl.java
trunk/user/test/com/google/gwt/user/server/rpc/ValueTypesTestServiceImpl.java
trunk/user/test/test/ServletMappingTestServiceImpl1.java
trunk/user/test/test/ServletMappingTestServiceImpl2.java
trunk/user/test/test/ServletMappingTestServiceImpl3.java
Log:
Initial add of derpc code base.
This code is experimental and should not be used in production apps.
Modified:
trunk/samples/dynatable/src/com/google/gwt/sample/dynatable/DynaTable.gwt.xml
==============================================================================
---
trunk/samples/dynatable/src/com/google/gwt/sample/dynatable/DynaTable.gwt.xml
(original)
+++
trunk/samples/dynatable/src/com/google/gwt/sample/dynatable/DynaTable.gwt.xml
Mon Jul 6 16:17:17 2009
@@ -13,7 +13,9 @@
<!-- limitations under the
License. -->
<module rename-to="dynatable">
- <inherits name='com.google.gwt.user.User' />
- <entry-point class='com.google.gwt.sample.dynatable.client.DynaTable' />
- <servlet path='/calendar'
class='com.google.gwt.sample.dynatable.server.SchoolCalendarServiceImpl'/>
+ <inherits name='com.google.gwt.user.User' />
+ <inherits name='com.google.gwt.rpc.RPC' />
+ <entry-point class='com.google.gwt.sample.dynatable.client.DynaTable' />
+ <servlet path='/calendar'
+
class='com.google.gwt.sample.dynatable.server.SchoolCalendarServiceImpl' />
</module>
Modified:
trunk/samples/dynatable/src/com/google/gwt/sample/dynatable/client/SchoolCalendarService.java
==============================================================================
---
trunk/samples/dynatable/src/com/google/gwt/sample/dynatable/client/SchoolCalendarService.java
(original)
+++
trunk/samples/dynatable/src/com/google/gwt/sample/dynatable/client/SchoolCalendarService.java
Mon Jul 6 16:17:17 2009
@@ -15,14 +15,13 @@
*/
package com.google.gwt.sample.dynatable.client;
-import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.rpc.client.RpcService;
/**
- * The interface for the RPC server endpoint to get school calendar
- * information.
+ * The interface for the RPC server endpoint to get school calendar
information.
*/
-public interface SchoolCalendarService extends RemoteService {
-
+public interface SchoolCalendarService extends RpcService {
+
Person[] getPeople(int startIndex, int maxCount);
-
+
}
Modified:
trunk/samples/dynatable/src/com/google/gwt/sample/dynatable/server/SchoolCalendarServiceImpl.java
==============================================================================
---
trunk/samples/dynatable/src/com/google/gwt/sample/dynatable/server/SchoolCalendarServiceImpl.java
(original)
+++
trunk/samples/dynatable/src/com/google/gwt/sample/dynatable/server/SchoolCalendarServiceImpl.java
Mon Jul 6 16:17:17 2009
@@ -21,7 +21,7 @@
import com.google.gwt.sample.dynatable.client.SchoolCalendarService;
import com.google.gwt.sample.dynatable.client.Student;
import com.google.gwt.sample.dynatable.client.TimeSlot;
-import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+import com.google.gwt.user.server.rpc.HybridServiceServlet;
import java.util.ArrayList;
import java.util.Arrays;
@@ -31,7 +31,7 @@
/**
* The implemenation of the RPC service which runs on the server.
*/
-public class SchoolCalendarServiceImpl extends RemoteServiceServlet
implements
+public class SchoolCalendarServiceImpl extends HybridServiceServlet
implements
SchoolCalendarService {
private static final String[] FIRST_NAMES = new String[] {
Modified: trunk/user/src/com/google/gwt/junit/server/JUnitHostImpl.java
==============================================================================
--- trunk/user/src/com/google/gwt/junit/server/JUnitHostImpl.java (original)
+++ trunk/user/src/com/google/gwt/junit/server/JUnitHostImpl.java Mon Jul
6 16:17:17 2009
@@ -24,8 +24,8 @@
import com.google.gwt.junit.client.impl.JUnitResult;
import com.google.gwt.junit.client.impl.StackTraceWrapper;
import com.google.gwt.user.client.rpc.InvocationException;
+import com.google.gwt.user.server.rpc.HybridServiceServlet;
import com.google.gwt.user.server.rpc.RPCServletUtils;
-import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import java.io.IOException;
import java.lang.reflect.Constructor;
@@ -40,7 +40,7 @@
* communication between the unit test code running in a browser and the
real
* test process.
*/
-public class JUnitHostImpl extends RemoteServiceServlet implements
JUnitHost {
+public class JUnitHostImpl extends HybridServiceServlet implements
JUnitHost {
/**
* A hook into GWTUnitTestShell, the underlying unit test process.
Added: trunk/user/src/com/google/gwt/rpc/RPC.gwt.xml
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/RPC.gwt.xml Mon Jul 6 16:17:17 2009
@@ -0,0 +1,25 @@
+<module>
+ <inherits name="com.google.gwt.core.Core" />
+ <inherits name="com.google.gwt.user.RemoteService" />
+
+ <source path="client" />
+ <super-source path="super" />
+
+ <define-linker name="clientOracleLinker"
+ class="com.google.gwt.rpc.linker.ClientOracleLinker" />
+ <add-linker name="clientOracleLinker" />
+
+ <define-property name="gwt.rpc.hijackLegacyInterface"
values="true,false" />
+ <set-property name="gwt.rpc.hijackLegacyInterface" value="false" />
+
+ <generate-with class="com.google.gwt.rpc.rebind.RpcServiceGenerator">
+ <any>
+ <when-type-assignable class="com.google.gwt.rpc.client.RpcService" />
+ <all>
+ <when-type-assignable
class="com.google.gwt.user.client.rpc.RemoteService" />
+ <when-property-is name="gwt.rpc.hijackLegacyInterface"
+ value="true" />
+ </all>
+ </any>
+ </generate-with>
+</module>
\ No newline at end of file
Added: trunk/user/src/com/google/gwt/rpc/client/RpcService.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/RpcService.java Mon Jul 6
16:17:17 2009
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+
+/**
+ * EXPERIMENTAL and subject to change. Do not use this in production code.
+ * <p>
+ * Marker interface to be used with RpcServlet.
+ */
+public interface RpcService extends RemoteService {
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/ArrayValueCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/ArrayValueCommand.java Mon
Jul 6 16:17:17 2009
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor.Context;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Encapsulates an array of values.
+ */
+public class ArrayValueCommand extends IdentityValueCommand {
+ private final Class<?> componentType;
+ private final List<ValueCommand> values = new ArrayList<ValueCommand>();
+
+ public ArrayValueCommand(Class<?> componentType) {
+ this.componentType = componentType;
+ }
+
+ public void add(ValueCommand x) {
+ values.add(x);
+ }
+
+ @Override
+ public void clear() {
+ values.clear();
+ }
+
+ public Class<?> getComponentType() {
+ return componentType;
+ }
+
+ public List<ValueCommand> getComponentValues() {
+ return values;
+ }
+
+ @Override
+ public void traverse(RpcCommandVisitor visitor, Context ctx) {
+ if (visitor.visit(this, ctx)) {
+ visitor.accept(values);
+ }
+ visitor.endVisit(this, ctx);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/BooleanValueCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/BooleanValueCommand.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor.Context;
+
+/**
+ * Encapsulates a boolean value in the command stream.
+ */
+public class BooleanValueCommand extends ScalarValueCommand {
+ private final boolean value;
+
+ public BooleanValueCommand(double value) {
+ this.value = value != 0;
+ }
+
+ public BooleanValueCommand(boolean value) {
+ this.value = value;
+ }
+
+ public BooleanValueCommand(Boolean value) {
+ this.value = value;
+ }
+
+ @Override
+ public Boolean getValue() {
+ return value;
+ }
+
+ @Override
+ public void traverse(RpcCommandVisitor visitor, Context ctx) {
+ visitor.visit(this, ctx);
+ visitor.endVisit(this, ctx);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/ByteValueCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/ByteValueCommand.java Mon
Jul 6 16:17:17 2009
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor.Context;
+
+/**
+ * Encapsulates a boolean value in the command stream.
+ */
+public class ByteValueCommand extends ScalarValueCommand {
+ private final byte value;
+
+ public ByteValueCommand(double value) {
+ this.value = (byte) value;
+ }
+
+ public ByteValueCommand(byte value) {
+ this.value = value;
+ }
+
+ public ByteValueCommand(Byte value) {
+ this.value = value;
+ }
+
+ @Override
+ public Byte getValue() {
+ return value;
+ }
+
+ @Override
+ public void traverse(RpcCommandVisitor visitor, Context ctx) {
+ visitor.visit(this, ctx);
+ visitor.endVisit(this, ctx);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/CharValueCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/CharValueCommand.java Mon
Jul 6 16:17:17 2009
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor.Context;
+
+/**
+ * Encapsulates a boolean value in the command stream.
+ */
+public class CharValueCommand extends ScalarValueCommand {
+ private final char value;
+
+ public CharValueCommand(double value) {
+ this.value = (char) value;
+ }
+
+ public CharValueCommand(char value) {
+ this.value = value;
+ }
+
+ public CharValueCommand(Character value) {
+ this.value = value;
+ }
+
+ @Override
+ public Character getValue() {
+ return value;
+ }
+
+ @Override
+ public void traverse(RpcCommandVisitor visitor, Context ctx) {
+ visitor.visit(this, ctx);
+ visitor.endVisit(this, ctx);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/CommandSink.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/CommandSink.java Mon Jul
6 16:17:17 2009
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+
+/**
+ * Accepts a stream of RpcCommands.
+ */
+public abstract class CommandSink {
+ /**
+ * Accept an RpcCommand for processing.
+ *
+ * @param command
+ * @throws SerializationException
+ */
+ public abstract void accept(RpcCommand command) throws
SerializationException;
+
+ /**
+ * Called when no more commands will be sent.
+ */
+ public abstract void finish() throws SerializationException;
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/DoubleValueCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/DoubleValueCommand.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor.Context;
+
+/**
+ * Encapsulates a boolean value in the command stream.
+ */
+public class DoubleValueCommand extends ScalarValueCommand {
+ private final double value;
+
+ public DoubleValueCommand(double value) {
+ this.value = value;
+ }
+
+ public DoubleValueCommand(Double value) {
+ this.value = value;
+ }
+
+ @Override
+ public Double getValue() {
+ return value;
+ }
+
+ @Override
+ public void traverse(RpcCommandVisitor visitor, Context ctx) {
+ visitor.visit(this, ctx);
+ visitor.endVisit(this, ctx);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/EnumValueCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/EnumValueCommand.java Mon
Jul 6 16:17:17 2009
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor.Context;
+
+/**
+ * Encapsulates a string value in the command stream.
+ */
+public class EnumValueCommand extends IdentityValueCommand {
+ private Enum<?> value;
+
+ public Enum<?> getValue() {
+ return value;
+ }
+
+ /**
+ * Not a constructor argument so that the identity of the
EnumValueCommand can
+ * be established before the identity of the value.
+ */
+ public void setValue(Enum<?> value) {
+ this.value = value;
+ }
+
+ @Override
+ public void traverse(RpcCommandVisitor visitor, Context ctx) {
+ visitor.visit(this, ctx);
+ visitor.endVisit(this, ctx);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/FloatValueCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/FloatValueCommand.java Mon
Jul 6 16:17:17 2009
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor.Context;
+
+/**
+ * Encapsulates a boolean value in the command stream.
+ */
+public class FloatValueCommand extends ScalarValueCommand {
+ private final float value;
+
+ public FloatValueCommand(double value) {
+ this.value = (float) value;
+ }
+
+ public FloatValueCommand(float value) {
+ this.value = value;
+ }
+
+ public FloatValueCommand(Float value) {
+ this.value = value;
+ }
+
+ @Override
+ public Float getValue() {
+ return value;
+ }
+
+ @Override
+ public void traverse(RpcCommandVisitor visitor, Context ctx) {
+ visitor.visit(this, ctx);
+ visitor.endVisit(this, ctx);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/HasSetters.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/HasSetters.java Mon Jul 6
16:17:17 2009
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import java.util.List;
+
+/**
+ * Describes RpcCommands that have additional field setters.
+ */
+public interface HasSetters {
+
+ List<SetCommand> getSetters();
+
+ void set(Class<?> fieldDeclClass, String fieldName, ValueCommand value);
+
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/HasTargetClass.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/HasTargetClass.java Mon
Jul 6 16:17:17 2009
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+/**
+ * An RpcCommand with a target class.
+ */
+public interface HasTargetClass {
+ Class<?> getTargetClass();
+}
\ No newline at end of file
Added: trunk/user/src/com/google/gwt/rpc/client/ast/HasValues.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/HasValues.java Mon Jul 6
16:17:17 2009
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import java.util.List;
+
+/**
+ * An RpcCommand that contains other values.
+ */
+public interface HasValues {
+
+ void addValue(ValueCommand value);
+
+ List<ValueCommand> getValues();
+
+}
Added:
trunk/user/src/com/google/gwt/rpc/client/ast/IdentityValueCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/IdentityValueCommand.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+/**
+ * Represents a hierarchy of value types that must maintain distinct object
+ * identity on the client. This type finalizes <code>equals</code> and
+ * <code>hashCode</code> to give subtypes identity equality sematics.
+ */
+public abstract class IdentityValueCommand extends ValueCommand {
+ @Override
+ public final boolean equals(Object o) {
+ return this == o;
+ }
+
+ @Override
+ public final int hashCode() {
+ return System.identityHashCode(this);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/InstantiateCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/InstantiateCommand.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor.Context;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Triggers the instantiation of an object.
+ */
+public class InstantiateCommand extends IdentityValueCommand implements
+ HasSetters, HasTargetClass {
+ private final Class<?> clazz;
+ private final List<SetCommand> setters = new ArrayList<SetCommand>();
+
+ public InstantiateCommand(Class<?> clazz) {
+ this.clazz = clazz;
+ }
+
+ @Override
+ public void clear() {
+ setters.clear();
+ }
+
+ public List<SetCommand> getSetters() {
+ return setters;
+ }
+
+ public Class<?> getTargetClass() {
+ return clazz;
+ }
+
+ public void set(Class<?> fieldDeclClass, String fieldName, ValueCommand
value) {
+ setters.add(new SetCommand(fieldDeclClass, fieldName, value));
+ }
+
+ @Override
+ public void traverse(RpcCommandVisitor visitor, Context ctx) {
+ if (visitor.visit(this, ctx)) {
+ visitor.accept(setters);
+ }
+ visitor.endVisit(this, ctx);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/IntValueCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/IntValueCommand.java Mon
Jul 6 16:17:17 2009
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor.Context;
+
+/**
+ * Encapsulates a int value in the command stream.
+ */
+public class IntValueCommand extends ScalarValueCommand {
+ private final int value;
+
+ public IntValueCommand(double value) {
+ this.value = (int) value;
+ }
+
+ public IntValueCommand(int value) {
+ this.value = value;
+ }
+
+ public IntValueCommand(Integer value) {
+ this.value = value;
+ }
+
+ @Override
+ public Integer getValue() {
+ return value;
+ }
+
+ @Override
+ public void traverse(RpcCommandVisitor visitor, Context ctx) {
+ visitor.visit(this, ctx);
+ visitor.endVisit(this, ctx);
+ }
+}
Added:
trunk/user/src/com/google/gwt/rpc/client/ast/InvokeCustomFieldSerializerCommand.java
==============================================================================
--- (empty file)
+++
trunk/user/src/com/google/gwt/rpc/client/ast/InvokeCustomFieldSerializerCommand.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor.Context;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A placeholder for custom logic invocation.
+ */
+public class InvokeCustomFieldSerializerCommand extends
IdentityValueCommand
+ implements HasSetters, HasTargetClass, HasValues {
+ private final Class<?> manuallySerializedType;
+ private final Class<?> serializer;
+ private final List<SetCommand> setters = new ArrayList<SetCommand>();
+ private final Class<?> instantiatedType;
+ private final List<ValueCommand> values = new ArrayList<ValueCommand>();
+
+ public InvokeCustomFieldSerializerCommand(Class<?> instantiatedType,
+ Class<?> serializer, Class<?> manuallySerializedType) {
+ this.instantiatedType = instantiatedType;
+ this.serializer = serializer;
+ this.manuallySerializedType = manuallySerializedType;
+ }
+
+ public void addValue(ValueCommand value) {
+ values.add(value);
+ }
+
+ @Override
+ public void clear() {
+ values.clear();
+ }
+
+ public Class<?> getManuallySerializedType() {
+ return manuallySerializedType;
+ }
+
+ public Class<?> getSerializerClass() {
+ return serializer;
+ }
+
+ public List<SetCommand> getSetters() {
+ return setters;
+ }
+
+ public Class<?> getTargetClass() {
+ return instantiatedType;
+ }
+
+ public List<ValueCommand> getValues() {
+ return values;
+ }
+
+ public void set(Class<?> fieldDeclClass, String fieldName, ValueCommand
value) {
+ setters.add(new SetCommand(fieldDeclClass, fieldName, value));
+ }
+
+ @Override
+ public void traverse(RpcCommandVisitor visitor, Context ctx) {
+ if (visitor.visit(this, ctx)) {
+ visitor.accept(values);
+ visitor.accept(setters);
+ }
+ visitor.endVisit(this, ctx);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/LongValueCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/LongValueCommand.java Mon
Jul 6 16:17:17 2009
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor.Context;
+
+/**
+ * Encapsulates a long value in the command stream.
+ */
+public class LongValueCommand extends ScalarValueCommand {
+ /*
+ * NB: This does not have a constructor that accepts a double; the
client must
+ * send the long
+ */
+ private final long value;
+
+ public LongValueCommand(long value) {
+ this.value = value;
+ }
+
+ public LongValueCommand(Long value) {
+ this.value = value;
+ }
+
+ @Override
+ public Long getValue() {
+ return value;
+ }
+
+ @Override
+ public void traverse(RpcCommandVisitor visitor, Context ctx) {
+ visitor.visit(this, ctx);
+ visitor.endVisit(this, ctx);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/NullValueCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/NullValueCommand.java Mon
Jul 6 16:17:17 2009
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor.Context;
+
+/**
+ * Represents a null or undefined value on the client.
+ */
+public class NullValueCommand extends ScalarValueCommand {
+ /**
+ * The singleton instance of NullValueCommand.
+ */
+ public static final NullValueCommand INSTANCE = new NullValueCommand();
+
+ private NullValueCommand() {
+ }
+
+ @Override
+ public Object getValue() {
+ return null;
+ }
+
+ @Override
+ public void traverse(RpcCommandVisitor visitor, Context ctx) {
+ visitor.visit(this, ctx);
+ visitor.endVisit(this, ctx);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/ReturnCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/ReturnCommand.java Mon
Jul 6 16:17:17 2009
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor.Context;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a return statement.
+ */
+public class ReturnCommand extends RpcCommand implements HasValues {
+ private final List<ValueCommand> values = new ArrayList<ValueCommand>();
+
+ public void addValue(ValueCommand value) {
+ values.add(value);
+ }
+
+ @Override
+ public void clear() {
+ values.clear();
+ }
+
+ public List<ValueCommand> getValues() {
+ return values;
+ }
+
+ @Override
+ public void traverse(RpcCommandVisitor visitor, Context ctx) {
+ if (visitor.visit(this, ctx)) {
+ visitor.accept(values);
+ }
+ visitor.endVisit(this, ctx);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/RpcCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/RpcCommand.java Mon Jul 6
16:17:17 2009
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor.Context;
+
+/**
+ * Encapsulates a basic operation to be performed in RPC command stream.
+ */
+public abstract class RpcCommand {
+ /**
+ * Delete all internal state so that the command may be used only for
instance
+ * tracking.
+ */
+ public void clear() {
+ }
+
+ public abstract void traverse(RpcCommandVisitor visitor, Context ctx);
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/RpcCommandVisitor.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/RpcCommandVisitor.java Mon
Jul 6 16:17:17 2009
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import java.util.List;
+
+/**
+ * Describes a visitor that will traverse the RpcCommand structures.
+ */
+public class RpcCommandVisitor {
+ /**
+ * The context provides no services at this point and is provided as an
+ * extension point for future development.
+ */
+ public interface Context {
+ }
+
+ public final void accept(List<? extends RpcCommand> x) {
+ doAccept(x);
+ }
+
+ public final RpcCommand accept(RpcCommand x) {
+ return doAccept(x);
+ }
+
+ public final void accept(RpcCommand[] x) {
+ doAccept(x);
+ }
+
+ public void endVisit(ArrayValueCommand x, Context ctx) {
+ }
+
+ public void endVisit(BooleanValueCommand x, Context ctx) {
+ }
+
+ public void endVisit(ByteValueCommand x, Context ctx) {
+ }
+
+ public void endVisit(CharValueCommand x, Context ctx) {
+ }
+
+ public void endVisit(DoubleValueCommand x, Context ctx) {
+ }
+
+ public void endVisit(EnumValueCommand x, Context ctx) {
+ }
+
+ public void endVisit(FloatValueCommand x, Context ctx) {
+ }
+
+ public void endVisit(InstantiateCommand x, Context ctx) {
+ }
+
+ public void endVisit(IntValueCommand x, Context ctx) {
+ }
+
+ public void endVisit(InvokeCustomFieldSerializerCommand x, Context ctx) {
+ }
+
+ public void endVisit(LongValueCommand x, Context ctx) {
+ }
+
+ public void endVisit(NullValueCommand x, Context ctx) {
+ }
+
+ public void endVisit(ReturnCommand x, Context ctx) {
+ }
+
+ public void endVisit(SetCommand x, Context ctx) {
+ }
+
+ public void endVisit(ShortValueCommand x, Context ctx) {
+ }
+
+ public void endVisit(StringValueCommand x, Context ctx) {
+ }
+
+ public void endVisit(ThrowCommand x, Context ctx) {
+ }
+
+ public boolean visit(ArrayValueCommand x, Context ctx) {
+ return true;
+ }
+
+ public boolean visit(BooleanValueCommand x, Context ctx) {
+ return true;
+ }
+
+ public boolean visit(ByteValueCommand x, Context ctx) {
+ return true;
+ }
+
+ public boolean visit(CharValueCommand x, Context ctx) {
+ return true;
+ }
+
+ public boolean visit(DoubleValueCommand x, Context ctx) {
+ return true;
+ }
+
+ public boolean visit(EnumValueCommand x, Context ctx) {
+ return true;
+ }
+
+ public boolean visit(FloatValueCommand x, Context ctx) {
+ return true;
+ }
+
+ public boolean visit(InstantiateCommand x, Context ctx) {
+ return true;
+ }
+
+ public boolean visit(IntValueCommand x, Context ctx) {
+ return true;
+ }
+
+ public boolean visit(InvokeCustomFieldSerializerCommand x, Context ctx) {
+ return true;
+ }
+
+ public boolean visit(LongValueCommand x, Context ctx) {
+ return true;
+ }
+
+ public boolean visit(NullValueCommand x, Context ctx) {
+ return true;
+ }
+
+ public boolean visit(ReturnCommand x, Context ctx) {
+ return true;
+ }
+
+ public boolean visit(SetCommand x, Context ctx) {
+ return true;
+ }
+
+ public boolean visit(ShortValueCommand x, Context ctx) {
+ return true;
+ }
+
+ public boolean visit(StringValueCommand x, Context ctx) {
+ return true;
+ }
+
+ public boolean visit(ThrowCommand x, Context ctx) {
+ return true;
+ }
+
+ protected void doAccept(List<? extends RpcCommand> x) {
+ for (RpcCommand c : x) {
+ accept(c);
+ }
+ }
+
+ protected RpcCommand doAccept(RpcCommand x) {
+ x.traverse(this, null);
+ return x;
+ }
+
+ protected void doAccept(RpcCommand[] x) {
+ for (RpcCommand c : x) {
+ accept(c);
+ }
+ }
+
+ /*
+ * TODO: Make this fail more visibly
+ */
+ protected final void halt(Throwable t) {
+ throw new RuntimeException("Unable to continue", t);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/ScalarValueCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/ScalarValueCommand.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+/**
+ * Represents a type hierarchy of values that do not maintain object
identity on
+ * the client.
+ */
+public abstract class ScalarValueCommand extends ValueCommand {
+ @Override
+ public final boolean equals(Object o) {
+ if (!(o instanceof ScalarValueCommand)) {
+ return false;
+ }
+ ScalarValueCommand other = (ScalarValueCommand) o;
+ Object myValue = getValue();
+ Object otherValue = other.getValue();
+ if (myValue == null && otherValue == null) {
+ return true;
+ } else if (myValue == null && otherValue != null) {
+ return false;
+ } else {
+ return myValue.equals(otherValue);
+ }
+ }
+
+ /**
+ * Returns the value represented by the ScalarValueCommand.
+ */
+ public abstract Object getValue();
+
+ @Override
+ public final int hashCode() {
+ return getValue() == null ? 0 : getValue().hashCode();
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/SetCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/SetCommand.java Mon Jul 6
16:17:17 2009
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor.Context;
+
+/**
+ * A command to set the value of an object in the command stream.
+ */
+public class SetCommand extends RpcCommand {
+ /*
+ * NB: Not using Field due to lack of GWT compatibility. Consider adding
the
+ * Field type to the GWT JRE code.
+ */
+ private String field;
+ /**
+ * The class that defines the field. Used to account for field shadowing.
+ */
+ private Class<?> fieldDeclClass;
+ private ValueCommand value;
+
+ public SetCommand(Class<?> fieldDeclClass, String field, ValueCommand
value) {
+ this.fieldDeclClass = fieldDeclClass;
+ this.field = field;
+ this.value = value;
+ }
+
+ @Override
+ public void clear() {
+ field = null;
+ value = null;
+ }
+
+ public String getField() {
+ return field;
+ }
+
+ public Class<?> getFieldDeclClass() {
+ return fieldDeclClass;
+ }
+
+ public ValueCommand getValue() {
+ return value;
+ }
+
+ @Override
+ public void traverse(RpcCommandVisitor visitor, Context ctx) {
+ if (visitor.visit(this, ctx)) {
+ value.traverse(visitor, ctx);
+ }
+ visitor.endVisit(this, ctx);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/ShortValueCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/ShortValueCommand.java Mon
Jul 6 16:17:17 2009
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor.Context;
+
+/**
+ * Encapsulates a short value in the command stream.
+ */
+public class ShortValueCommand extends ScalarValueCommand {
+ private final short value;
+
+ public ShortValueCommand(double value) {
+ this.value = (short) value;
+ }
+
+ public ShortValueCommand(short value) {
+ this.value = value;
+ }
+
+ public ShortValueCommand(Short value) {
+ this.value = value;
+ }
+
+ @Override
+ public Short getValue() {
+ return value;
+ }
+
+ @Override
+ public void traverse(RpcCommandVisitor visitor, Context ctx) {
+ visitor.visit(this, ctx);
+ visitor.endVisit(this, ctx);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/StringValueCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/StringValueCommand.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor.Context;
+
+/**
+ * Encapsulates a string value in the command stream.
+ */
+public class StringValueCommand extends ScalarValueCommand {
+ private final String value;
+
+ public StringValueCommand(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public void traverse(RpcCommandVisitor visitor, Context ctx) {
+ visitor.visit(this, ctx);
+ visitor.endVisit(this, ctx);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/ThrowCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/ThrowCommand.java Mon Jul
6 16:17:17 2009
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor.Context;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A command to indicate that a value was thrown by the remote side.
+ */
+public class ThrowCommand extends RpcCommand implements HasValues {
+ private ValueCommand toThrow;
+
+ public void addValue(ValueCommand value) {
+ assert toThrow == null;
+ toThrow = value;
+ }
+
+ public ValueCommand getThrownValue() {
+ return toThrow;
+ }
+
+ public List<ValueCommand> getValues() {
+ return Collections.singletonList(toThrow);
+ }
+
+ @Override
+ public void traverse(RpcCommandVisitor visitor, Context ctx) {
+ if (visitor.visit(this, ctx)) {
+ visitor.accept(toThrow);
+ }
+ visitor.endVisit(this, ctx);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/ast/ValueCommand.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/ast/ValueCommand.java Mon Jul
6 16:17:17 2009
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.ast;
+
+/**
+ * Represents an atomic value.
+ */
+public abstract class ValueCommand extends RpcCommand {
+ protected ValueCommand() {
+ }
+}
Added:
trunk/user/src/com/google/gwt/rpc/client/impl/ClientWriterFactory.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/impl/ClientWriterFactory.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.impl;
+
+import com.google.gwt.rpc.client.ast.CommandSink;
+import com.google.gwt.rpc.server.CommandServerSerializationStreamReader;
+import com.google.gwt.rpc.server.CommandServerSerializationStreamWriter;
+import com.google.gwt.rpc.server.HostedModeClientOracle;
+import com.google.gwt.rpc.server.SimplePayloadDecoder;
+import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+
+import java.util.Collections;
+
+/**
+ * Isolates client code from swapping out the command factory in hosted
versus
+ * web mode. This type has a super-source peer which is used in web mode.
+ */
+public class ClientWriterFactory {
+
+ public static SerializationStreamReader createReader(String payload)
+ throws IncompatibleRemoteServiceException, RemoteException {
+ SimplePayloadDecoder decoder;
+ try {
+ decoder = new SimplePayloadDecoder(new HostedModeClientOracle(),
payload);
+ } catch (ClassNotFoundException e) {
+ throw new IncompatibleRemoteServiceException(
+ "Client does not have a type sent by the server", e);
+ }
+ CommandServerSerializationStreamReader reader = new
CommandServerSerializationStreamReader();
+ if (decoder.getThrownValue() != null) {
+
reader.prepareToRead(Collections.singletonList(decoder.getThrownValue()));
+ try {
+ throw new RemoteException((Throwable) reader.readObject());
+ } catch (ClassCastException e) {
+ throw new RemoteException(
+ "The remote end threw something other than a Throwable", e);
+ } catch (SerializationException e) {
+ throw new RemoteException(
+ "The remote end threw an exception which could not be
deserialized",
+ e);
+ }
+ } else {
+ reader.prepareToRead(decoder.getValues());
+ }
+ return reader;
+ }
+
+ public static SerializationStreamWriter createWriter(
+ TypeOverrides typeOverrides, CommandSink commandSink) {
+ return new CommandServerSerializationStreamWriter(commandSink);
+ }
+}
Added:
trunk/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamReader.java
==============================================================================
--- (empty file)
+++
trunk/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamReader.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.impl;
+
+import com.google.gwt.core.client.JavaScriptException;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.core.client.UnsafeNativeLong;
+import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+
+/**
+ * A peer to CommandServerSerializationStreamWriter.
+ */
+public class CommandClientSerializationStreamReader implements
+ SerializationStreamReader {
+
+ private static native JsArray<JavaScriptObject> eval(String payload) /*-{
+ return eval(payload);
+ }-*/;
+
+ private static native boolean readBoolean0(JavaScriptObject obj, int
idx) /*-{
+ return !!obj[idx];
+ }-*/;
+
+ private static native byte readByte0(JavaScriptObject obj, int idx) /*-{
+ return obj[idx];
+ }-*/;
+
+ private static native char readChar0(JavaScriptObject obj, int idx) /*-{
+ return obj[idx];
+ }-*/;
+
+ private static native double readDouble0(JavaScriptObject obj, int idx)
/*-{
+ return obj[idx];
+ }-*/;
+
+ private static native float readFloat0(JavaScriptObject obj, int idx)
/*-{
+ return obj[idx];
+ }-*/;
+
+ private static native int readInt0(JavaScriptObject obj, int idx) /*-{
+ return obj[idx];
+ }-*/;
+
+ @UnsafeNativeLong
+ private static native long readLong0(JavaScriptObject obj, int idx) /*-{
+ return obj[idx];
+ }-*/;
+
+ private static native Object readObject0(JavaScriptObject obj, int idx)
/*-{
+ return obj[idx];
+ }-*/;
+
+ private static native short readShort0(JavaScriptObject obj, int idx)
/*-{
+ return obj[idx];
+ }-*/;
+
+ private static native String readString0(JavaScriptObject obj, int idx)
/*-{
+ return obj[idx];
+ }-*/;
+
+ // This field may be reset externally
+ volatile int idx = 0;
+ volatile JsArray<JavaScriptObject> payload;
+
+ public void prepareToRead(String js) throws RemoteException {
+ try {
+ payload = eval("(function(){var _={};" + js + "})()");
+ assert payload != null : "Payload evaluated to null";
+ } catch (JavaScriptException e) {
+ throw new IncompatibleRemoteServiceException(
+ "Unable to evaluate payload", e);
+ } catch (Throwable e) {
+ throw new RemoteException("Unable to evaluate payload", e);
+ }
+ }
+
+ public boolean readBoolean() throws SerializationException {
+ assert idx < payload.length() : "Attempting to read beyond end of
payload";
+ return readBoolean0(payload, idx++);
+ }
+
+ public byte readByte() throws SerializationException {
+ assert idx < payload.length() : "Attempting to read beyond end of
payload";
+ return readByte0(payload, idx++);
+ }
+
+ public char readChar() throws SerializationException {
+ assert idx < payload.length() : "Attempting to read beyond end of
payload";
+ return readChar0(payload, idx++);
+ }
+
+ public double readDouble() throws SerializationException {
+ assert idx < payload.length() : "Attempting to read beyond end of
payload";
+ return readDouble0(payload, idx++);
+ }
+
+ public float readFloat() throws SerializationException {
+ assert idx < payload.length() : "Attempting to read beyond end of
payload";
+ return readFloat0(payload, idx++);
+ }
+
+ public int readInt() throws SerializationException {
+ assert idx < payload.length() : "Attempting to read beyond end of
payload";
+ return readInt0(payload, idx++);
+ }
+
+ public long readLong() throws SerializationException {
+ assert idx < payload.length() : "Attempting to read beyond end of
payload";
+ return readLong0(payload, idx++);
+ }
+
+ public Object readObject() throws SerializationException {
+ assert idx < payload.length() : "Attempting to read beyond end of
payload";
+ return readObject0(payload, idx++);
+ }
+
+ public short readShort() throws SerializationException {
+ assert idx < payload.length() : "Attempting to read beyond end of
payload";
+ return readShort0(payload, idx++);
+ }
+
+ public String readString() throws SerializationException {
+ assert idx < payload.length() : "Attempting to read beyond end of
payload";
+ return readString0(payload, idx++);
+ }
+}
Added:
trunk/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamWriter.java
==============================================================================
--- (empty file)
+++
trunk/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamWriter.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.impl;
+
+import com.google.gwt.core.client.UnsafeNativeLong;
+import com.google.gwt.rpc.client.ast.ArrayValueCommand;
+import com.google.gwt.rpc.client.ast.BooleanValueCommand;
+import com.google.gwt.rpc.client.ast.CharValueCommand;
+import com.google.gwt.rpc.client.ast.CommandSink;
+import com.google.gwt.rpc.client.ast.DoubleValueCommand;
+import com.google.gwt.rpc.client.ast.EnumValueCommand;
+import com.google.gwt.rpc.client.ast.HasSetters;
+import com.google.gwt.rpc.client.ast.IdentityValueCommand;
+import com.google.gwt.rpc.client.ast.InstantiateCommand;
+import com.google.gwt.rpc.client.ast.InvokeCustomFieldSerializerCommand;
+import com.google.gwt.rpc.client.ast.LongValueCommand;
+import com.google.gwt.rpc.client.ast.NullValueCommand;
+import com.google.gwt.rpc.client.ast.StringValueCommand;
+import com.google.gwt.rpc.client.ast.ValueCommand;
+import com.google.gwt.rpc.client.impl.TypeOverrides.SerializeFunction;
+import com.google.gwt.user.client.rpc.IsSerializable;
+import com.google.gwt.user.client.rpc.SerializationException;
+
+import java.io.Serializable;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ * Provides a facade around serialization logic in client code.
+ */
+public class CommandClientSerializationStreamWriter extends
+ CommandSerializationStreamWriterBase {
+
+ private static Object anObject = new Object[] {};
+
+ static {
+ // Don't need to explicitly filter $H
+ anObject.hashCode();
+ }
+
+ private final Map<Object, IdentityValueCommand> identityMap = new
IdentityHashMap<Object, IdentityValueCommand>();
+ private final TypeOverrides serializer;
+
+ public CommandClientSerializationStreamWriter(TypeOverrides serializer,
+ CommandSink sink) {
+ super(sink);
+ this.serializer = serializer;
+ }
+
+ /**
+ * Type is passed in to handle primitive types.
+ */
+ protected ValueCommand makeValue(Class<?> type, Object value)
+ throws SerializationException {
+ SerializeFunction customSerializer;
+ ValueCommand toReturn;
+
+ if (value == null) {
+ toReturn = NullValueCommand.INSTANCE;
+ } else if (type.isPrimitive()) {
+ if (type == boolean.class) {
+ toReturn = new BooleanValueCommand((Boolean) value);
+ } else if (type == void.class) {
+ toReturn = NullValueCommand.INSTANCE;
+ } else if (type == long.class) {
+ toReturn = new LongValueCommand((Long) value);
+ } else if (type == char.class) {
+ toReturn = new CharValueCommand((Character) value);
+ } else {
+ assert value instanceof Number : "Expecting Number; had "
+ + value.getClass().getName();
+ toReturn = new DoubleValueCommand(((Number) value).doubleValue());
+ }
+
+ } else if ((toReturn = identityMap.get(value)) != null) {
+ // Fall through
+
+ } else if (type == String.class) {
+ toReturn = new StringValueCommand((String) value);
+
+ } else if (type.isArray()) {
+ ArrayValueCommand array = new
ArrayValueCommand(type.getComponentType());
+ identityMap.put(value, array);
+ extractData(array, value);
+ toReturn = array;
+
+ } else if (value instanceof Enum) {
+ EnumValueCommand e = new EnumValueCommand();
+ e.setValue((Enum<?>) value);
+ toReturn = e;
+
+ } else if ((customSerializer =
serializer.getOverride(type.getName())) != null) {
+ toReturn = invokeCustomSerializer(customSerializer, type, value);
+
+ } else {
+ toReturn = makeObject(type, value);
+ }
+
+ return toReturn;
+ }
+
+ private native void extractData(ArrayValueCommand x, Object obj) /*-{
+ for (var i = 0, j = obj.length; i < j; i++) {
+ var value =
this.@com.google.gwt.rpc.client.impl.CommandClientSerializationStreamWriter::makeValue(Ljava/lang/Object;)(obj[i]);
+
x.@com.google.gwt.rpc.client.ast.ArrayValueCommand::add(Lcom/google/gwt/rpc/client/ast/ValueCommand;)(value);
+ }
+ }-*/;
+
+ private native void extractData(HasSetters x, Object obj) /*-{
+ for (var key in obj) {
+ // Ignore common properties
+ if (key in
@com.google.gwt.rpc.client.impl.CommandClientSerializationStreamWriter::anObject)
{
+ continue;
+ }
+
this.@com.google.gwt.rpc.client.impl.CommandClientSerializationStreamWriter::extractField(Lcom/google/gwt/rpc/client/ast/HasSetters;Ljava/lang/Object;Ljava/lang/String;)(x,obj,key);
+ }
+ }-*/;
+
+ private native void extractField(HasSetters x, Object obj, String key)
/*-{
+ var command =
this.@com.google.gwt.rpc.client.impl.CommandClientSerializationStreamWriter::makeValue(Ljava/lang/Object;)(obj[key]);
+
+ // makeValue may return undefined
+ command &&
x.@com.google.gwt.rpc.client.ast.HasSetters::set(Ljava/lang/Class;Ljava/lang/String;Lcom/google/gwt/rpc/client/ast/ValueCommand;)(null,
key, command);
+ }-*/;
+
+ private ValueCommand invokeCustomSerializer(
+ SerializeFunction serializeFunction, Class<?> type, Object value)
+ throws SerializationException {
+ InvokeCustomFieldSerializerCommand command = new
InvokeCustomFieldSerializerCommand(
+ type, null, null);
+ identityMap.put(value, command);
+ CommandClientSerializationStreamWriter subWriter = new
CommandClientSerializationStreamWriter(
+ serializer, new HasValuesCommandSink(command));
+
+ serializeFunction.serialize(subWriter, value);
+ if (serializer.hasExtraFields(type.getName())) {
+ for (String extraField : serializer.getExtraFields(type.getName())) {
+ if (extraField != null) {
+ // Sometimes fields might be pruned
+ extractField(command, value, extraField);
+ }
+ }
+ }
+ return command;
+ }
+
+ private ValueCommand makeObject(Class<?> clazz, Object value)
+ throws SerializationException {
+ if (!(value instanceof Serializable || value instanceof
IsSerializable)) {
+ throw new SerializationException(clazz.getName()
+ + " is not a Serializable type");
+ }
+ InstantiateCommand x = new InstantiateCommand(clazz);
+ identityMap.put(value, x);
+
+ if (serializer.hasExtraFields(clazz.getName())) {
+ // Objects with transient fields or non-trivial semantics
+ for (String fieldName : serializer.getExtraFields(clazz.getName())) {
+ extractField(x, value, fieldName);
+ }
+ } else {
+ // Just a for-in loop
+ extractData(x, value);
+ }
+ return x;
+ }
+
+ @SuppressWarnings("unused")
+ @UnsafeNativeLong
+ private native ValueCommand makeValue(Object value) /*-{
+ var type;
+ if (value) {
+ // Maybe turn objects into primitives
+ value.valueOf && (value = value.valueOf());
+
+ // See if the value is our web-mode representation of a long
+ if (!value.@java.lang.Object::typeMarker && value.length &&
+ value.length == 2 && (typeof value[0] == 'number') && (typeof
value[1] == 'number')) {
+ type = 'long';
+ }
+ }
+ type || (type = typeof value);
+
+ switch (type) {
+ case 'boolean':
+ return
@com.google.gwt.rpc.client.ast.BooleanValueCommand::new(Z)(value);
+
+ case 'number':
+ return
@com.google.gwt.rpc.client.ast.DoubleValueCommand::new(D)(value);
+
+ case 'string':
+ return
@com.google.gwt.rpc.client.ast.StringValueCommand::new(Ljava/lang/String;)(value);
+
+ case 'long':
+ return
@com.google.gwt.rpc.client.ast.LongValueCommand::new(J)(value);
+
+ case 'function':
+ // Not serializable
+ break;
+
+ case 'object':
+ // typeof null == 'object'
+ if (!value) {
+ return @com.google.gwt.rpc.client.ast.NullValueCommand::INSTANCE;
+ }
+
+ if (!value.@java.lang.Object::typeMarker) {
+ // Not a Java object
+ break;
+ }
+
+ return
this.@com.google.gwt.rpc.client.impl.CommandClientSerializationStreamWriter::makeValue(Ljava/lang/Class;Ljava/lang/Object;)(value.@java.lang.Object::getClass()(),
value);
+
+ case 'undefined':
+ // typeof undefined == 'undefined', but we treat it as null
+ return @com.google.gwt.rpc.client.ast.NullValueCommand::INSTANCE;
+
+ default:
+ throw
@java.lang.RuntimeException::new(Ljava/lang/String;)('Unknown type ' +
type);
+ }
+
+ // Intentionally return undefined
+ }-*/;
+}
Added:
trunk/user/src/com/google/gwt/rpc/client/impl/CommandSerializationStreamWriterBase.java
==============================================================================
--- (empty file)
+++
trunk/user/src/com/google/gwt/rpc/client/impl/CommandSerializationStreamWriterBase.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.impl;
+
+import com.google.gwt.rpc.client.ast.CommandSink;
+import com.google.gwt.rpc.client.ast.ValueCommand;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+
+/**
+ * Contains base methods for implementing a SerializationStreamWriter.
+ */
+public abstract class CommandSerializationStreamWriterBase implements
+ SerializationStreamWriter {
+
+ private final CommandSink commandSink;
+
+ protected CommandSerializationStreamWriterBase(CommandSink sink) {
+ this.commandSink = sink;
+ }
+
+ public void writeBoolean(boolean value) throws SerializationException {
+ commandSink.accept(makeValue(boolean.class, value));
+ }
+
+ public void writeByte(byte value) throws SerializationException {
+ commandSink.accept(makeValue(byte.class, value));
+ }
+
+ public void writeChar(char value) throws SerializationException {
+ commandSink.accept(makeValue(char.class, value));
+ }
+
+ public void writeDouble(double value) throws SerializationException {
+ commandSink.accept(makeValue(double.class, value));
+ }
+
+ public void writeFloat(float value) throws SerializationException {
+ commandSink.accept(makeValue(float.class, value));
+ }
+
+ public void writeInt(int value) throws SerializationException {
+ commandSink.accept(makeValue(int.class, value));
+ }
+
+ public void writeLong(long value) throws SerializationException {
+ commandSink.accept(makeValue(long.class, value));
+ }
+
+ public void writeObject(Object instance) throws SerializationException {
+ commandSink.accept(makeValue(instance != null ? instance.getClass()
+ : void.class, instance));
+ }
+
+ public void writeShort(short value) throws SerializationException {
+ commandSink.accept(makeValue(short.class, value));
+ }
+
+ public void writeString(String value) throws SerializationException {
+ commandSink.accept(makeValue(String.class, value));
+ }
+
+ protected final CommandSink getCommandSink() {
+ return commandSink;
+ }
+
+ protected abstract ValueCommand makeValue(Class<?> type, Object value)
+ throws SerializationException;
+
+}
\ No newline at end of file
Added:
trunk/user/src/com/google/gwt/rpc/client/impl/CommandToStringWriter.java
==============================================================================
--- (empty file)
+++
trunk/user/src/com/google/gwt/rpc/client/impl/CommandToStringWriter.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.impl;
+
+import com.google.gwt.rpc.client.ast.CommandSink;
+import com.google.gwt.rpc.client.ast.ReturnCommand;
+import com.google.gwt.rpc.client.ast.RpcCommand;
+import com.google.gwt.rpc.client.ast.ValueCommand;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+
+/**
+ * Existing code assumes that a SerializationStreamWriter will export its
+ * payload via the toString() method. This uses an internal CommandSink and
+ * creates a string payload on-demand.
+ */
+public class CommandToStringWriter implements SerializationStreamWriter {
+
+ private static class ToStringCommandSink extends CommandSink {
+ private final ReturnCommand retCommand = new ReturnCommand();
+
+ @Override
+ public void accept(RpcCommand command) throws SerializationException {
+ retCommand.addValue((ValueCommand) command);
+ }
+
+ @Override
+ public void finish() throws SerializationException {
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ SimplePayloadSink sink = new SimplePayloadSink(sb);
+ try {
+ sink.accept(retCommand);
+ sink.finish();
+ } catch (SerializationException e) {
+ throw new RuntimeException("Unable to create payload", e);
+ }
+ return sb.toString();
+ }
+ }
+
+ private final ToStringCommandSink commandSink = new
ToStringCommandSink();
+ private final SerializationStreamWriter writer;
+
+ public CommandToStringWriter(TypeOverrides overrides) {
+ writer = ClientWriterFactory.createWriter(overrides, commandSink);
+ }
+
+ @Override
+ public String toString() {
+ return commandSink.toString();
+ }
+
+ public void writeBoolean(boolean value) throws SerializationException {
+ writer.writeBoolean(value);
+ }
+
+ public void writeByte(byte value) throws SerializationException {
+ writer.writeByte(value);
+ }
+
+ public void writeChar(char value) throws SerializationException {
+ writer.writeChar(value);
+ }
+
+ public void writeDouble(double value) throws SerializationException {
+ writer.writeDouble(value);
+ }
+
+ public void writeFloat(float value) throws SerializationException {
+ writer.writeFloat(value);
+ }
+
+ public void writeInt(int value) throws SerializationException {
+ writer.writeInt(value);
+ }
+
+ public void writeLong(long value) throws SerializationException {
+ writer.writeLong(value);
+ }
+
+ public void writeObject(Object value) throws SerializationException {
+ writer.writeObject(value);
+ }
+
+ public void writeShort(short value) throws SerializationException {
+ writer.writeShort(value);
+ }
+
+ public void writeString(String value) throws SerializationException {
+ writer.writeString(value);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/impl/EscapeUtil.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/impl/EscapeUtil.java Mon Jul
6 16:17:17 2009
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.impl;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.rpc.impl.ClientSerializationStreamWriter;
+import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter;
+
+/**
+ * Provides the JVM implementations for payload escaping.
+ */
+public class EscapeUtil {
+ public static String escape(String payload) {
+ if (GWT.isClient()) {
+ return ClientSerializationStreamWriter.quoteString(payload);
+ } else {
+ String quoted =
ServerSerializationStreamWriter.escapeString(payload);
+ return quoted.substring(1, quoted.length() - 1);
+ }
+ }
+}
Added:
trunk/user/src/com/google/gwt/rpc/client/impl/HasValuesCommandSink.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/impl/HasValuesCommandSink.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.impl;
+
+import com.google.gwt.rpc.client.ast.CommandSink;
+import com.google.gwt.rpc.client.ast.HasValues;
+import com.google.gwt.rpc.client.ast.RpcCommand;
+import com.google.gwt.rpc.client.ast.ValueCommand;
+import com.google.gwt.user.client.rpc.SerializationException;
+
+/**
+ * A simple CommandSink that adds observed values to a container command.
+ */
+public class HasValuesCommandSink extends CommandSink {
+ private HasValues container;
+
+ public HasValuesCommandSink(HasValues container) {
+ this.container = container;
+ }
+
+ @Override
+ public void accept(RpcCommand command) throws SerializationException {
+ if (!(command instanceof ValueCommand)) {
+ throw new SerializationException(command.getClass().getName());
+ }
+ container.addValue((ValueCommand) command);
+ }
+
+ /**
+ * Does nothing.
+ */
+ @Override
+ public void finish() throws SerializationException {
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/impl/ListCommandSink.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/impl/ListCommandSink.java Mon
Jul 6 16:17:17 2009
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.impl;
+
+import com.google.gwt.rpc.client.ast.CommandSink;
+import com.google.gwt.rpc.client.ast.RpcCommand;
+import com.google.gwt.user.client.rpc.SerializationException;
+
+import java.util.List;
+
+/**
+ * A simple CommandSink that adds observed commands to a list.
+ */
+public class ListCommandSink extends CommandSink {
+ private List<RpcCommand> commands;
+
+ public ListCommandSink(List<RpcCommand> commands) {
+ this.commands = commands;
+ }
+
+ @Override
+ public void accept(RpcCommand command) throws SerializationException {
+ commands.add(command);
+ }
+
+ /**
+ * Does nothing.
+ */
+ @Override
+ public void finish() throws SerializationException {
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/impl/RemoteException.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/impl/RemoteException.java Mon
Jul 6 16:17:17 2009
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.impl;
+
+/**
+ * A wrapper exception type indicating that the remote end threw an
exception
+ * over the wire.
+ */
+public class RemoteException extends RuntimeException {
+ public RemoteException() {
+ }
+
+ public RemoteException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+ public RemoteException(Throwable cause) {
+ super(cause);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/impl/RpcCallbackAdapter.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/impl/RpcCallbackAdapter.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.impl;
+
+import com.google.gwt.http.client.Request;
+import com.google.gwt.http.client.RequestCallback;
+import com.google.gwt.http.client.Response;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
+import com.google.gwt.user.client.rpc.InvocationException;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamFactory;
+import com.google.gwt.user.client.rpc.StatusCodeException;
+import com.google.gwt.user.client.rpc.impl.RemoteServiceProxy;
+
+/**
+ * Adapter from a {@link RequestCallback} interface to an {@link
AsyncCallback}
+ * interface.
+ *
+ * For internal use only.
+ *
+ * @param <T> the type parameter for the {@link AsyncCallback}
+ */
+public class RpcCallbackAdapter<T> implements RequestCallback {
+
+ /**
+ * {@link AsyncCallback} to notify or success or failure.
+ */
+ private final AsyncCallback<T> callback;
+
+ /**
+ * Used for stats recording.
+ */
+ private final String methodName;
+
+ /**
+ * Used for stats recording.
+ */
+ private final int requestId;
+
+ private final SerializationStreamFactory streamFactory;
+
+ public RpcCallbackAdapter(SerializationStreamFactory streamFactory,
+ String methodName, int requestId, AsyncCallback<T> callback) {
+ assert (streamFactory != null);
+ assert (callback != null);
+
+ this.streamFactory = streamFactory;
+ this.callback = callback;
+ this.methodName = methodName;
+ this.requestId = requestId;
+ }
+
+ public void onError(Request request, Throwable exception) {
+ callback.onFailure(exception);
+ }
+
+ @SuppressWarnings(value = {"unchecked", "unused"})
+ public void onResponseReceived(Request request, Response response) {
+ T result = null;
+ Throwable caught = null;
+ try {
+ String encodedResponse = response.getText();
+ int statusCode = response.getStatusCode();
+ boolean toss = RemoteServiceProxy.isStatsAvailable()
+ &&
RemoteServiceProxy.stats(RemoteServiceProxy.bytesStat(methodName,
+ requestId, encodedResponse.length(), "responseReceived"));
+
+ if (statusCode != Response.SC_OK) {
+ caught = new StatusCodeException(statusCode, encodedResponse);
+ } else if (encodedResponse == null) {
+ // This can happen if the XHR is interrupted by the server dying
+ caught = new InvocationException("No response payload");
+ } else {
+ result = (T)
streamFactory.createStreamReader(encodedResponse).readObject();
+ }
+ } catch (RemoteException e) {
+ caught = e.getCause();
+ } catch (SerializationException e) {
+ caught = new IncompatibleRemoteServiceException(
+ "The response could not be deserialized", e);
+ } catch (Throwable e) {
+ caught = e;
+ } finally {
+ boolean toss = RemoteServiceProxy.isStatsAvailable()
+ &&
RemoteServiceProxy.stats(RemoteServiceProxy.timeStat(methodName,
+ requestId, "responseDeserialized"));
+ }
+
+ try {
+ if (caught == null) {
+ callback.onSuccess(result);
+ } else {
+ callback.onFailure(caught);
+ }
+ } finally {
+ boolean toss = RemoteServiceProxy.isStatsAvailable()
+ &&
RemoteServiceProxy.stats(RemoteServiceProxy.timeStat(methodName,
+ requestId, "end"));
+ }
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/impl/RpcServiceProxy.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/impl/RpcServiceProxy.java Mon
Jul 6 16:17:17 2009
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.impl;
+
+import com.google.gwt.http.client.RequestCallback;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import com.google.gwt.user.client.rpc.impl.RemoteServiceProxy;
+import
com.google.gwt.user.client.rpc.impl.RequestCallbackAdapter.ResponseReader;
+
+/**
+ * The base type for RPC proxies.
+ */
+public class RpcServiceProxy extends RemoteServiceProxy {
+ private final TypeOverrides typeOverrides;
+
+ protected RpcServiceProxy(String moduleBaseURL,
+ String remoteServiceRelativePath, TypeOverrides typeOverrides) {
+ super(moduleBaseURL, remoteServiceRelativePath, null, null);
+ this.typeOverrides = typeOverrides;
+ }
+
+ @Override
+ public SerializationStreamReader createStreamReader(String encoded)
+ throws SerializationException, RemoteException {
+ return ClientWriterFactory.createReader(encoded);
+ }
+
+ @Override
+ public SerializationStreamWriter createStreamWriter() {
+ return new CommandToStringWriter(typeOverrides);
+ }
+
+ @Override
+ protected <T> RequestCallback doCreateRequestCallback(
+ ResponseReader responseReader, String methodName, int
invocationCount,
+ AsyncCallback<T> callback) {
+ return new RpcCallbackAdapter<T>(this, methodName, invocationCount,
+ callback);
+ }
+
+}
Added: trunk/user/src/com/google/gwt/rpc/client/impl/SimplePayloadSink.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/impl/SimplePayloadSink.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.impl;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.rpc.client.ast.ArrayValueCommand;
+import com.google.gwt.rpc.client.ast.BooleanValueCommand;
+import com.google.gwt.rpc.client.ast.ByteValueCommand;
+import com.google.gwt.rpc.client.ast.CharValueCommand;
+import com.google.gwt.rpc.client.ast.CommandSink;
+import com.google.gwt.rpc.client.ast.DoubleValueCommand;
+import com.google.gwt.rpc.client.ast.EnumValueCommand;
+import com.google.gwt.rpc.client.ast.FloatValueCommand;
+import com.google.gwt.rpc.client.ast.InstantiateCommand;
+import com.google.gwt.rpc.client.ast.IntValueCommand;
+import com.google.gwt.rpc.client.ast.InvokeCustomFieldSerializerCommand;
+import com.google.gwt.rpc.client.ast.LongValueCommand;
+import com.google.gwt.rpc.client.ast.NullValueCommand;
+import com.google.gwt.rpc.client.ast.ReturnCommand;
+import com.google.gwt.rpc.client.ast.RpcCommand;
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor;
+import com.google.gwt.rpc.client.ast.SetCommand;
+import com.google.gwt.rpc.client.ast.ShortValueCommand;
+import com.google.gwt.rpc.client.ast.StringValueCommand;
+import com.google.gwt.rpc.client.ast.ThrowCommand;
+import com.google.gwt.rpc.client.ast.ValueCommand;
+import com.google.gwt.user.client.rpc.SerializationException;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This implementation of CommandSink encodes RpcCommands in a simple
transport
+ * format that can be interpreted by both the client and the server.
+ */
+public class SimplePayloadSink extends CommandSink {
+
+ private class Visitor extends RpcCommandVisitor {
+
+ @Override
+ public void endVisit(BooleanValueCommand x, Context ctx) {
+ appendTypedData(BOOLEAN_TYPE, x.getValue() ? "1" : "0");
+ }
+
+ @Override
+ public void endVisit(ByteValueCommand x, Context ctx) {
+ appendTypedData(BYTE_TYPE, x.getValue().toString());
+ }
+
+ @Override
+ public void endVisit(CharValueCommand x, Context ctx) {
+ appendTypedData(CHAR_TYPE, String.valueOf((int) x.getValue()));
+ }
+
+ @Override
+ public void endVisit(DoubleValueCommand x, Context ctx) {
+ appendTypedData(DOUBLE_TYPE, x.getValue().toString());
+ }
+
+ @Override
+ public void endVisit(EnumValueCommand x, Context ctx) {
+ // ETypeSeedName~"9~FieldName
+ if (appendIdentity(x)) {
+ appendTypedData(ENUM_TYPE,
x.getValue().getDeclaringClass().getName());
+ accept(new StringValueCommand(x.getValue().name()));
+ }
+ }
+
+ @Override
+ public void endVisit(FloatValueCommand x, Context ctx) {
+ appendTypedData(FLOAT_TYPE, x.getValue().toString());
+ }
+
+ @Override
+ public void endVisit(IntValueCommand x, Context ctx) {
+ appendTypedData(INT_TYPE, x.getValue().toString());
+ }
+
+ @Override
+ public void endVisit(LongValueCommand x, Context ctx) {
+ appendTypedData(LONG_TYPE, x.getValue().toString());
+ }
+
+ @Override
+ public void endVisit(NullValueCommand x, Context ctx) {
+ appendTypedData(VOID_TYPE, "");
+ }
+
+ @Override
+ public void endVisit(ShortValueCommand x, Context ctx) {
+ appendTypedData(SHORT_TYPE, x.getValue().toString());
+ }
+
+ @Override
+ public void endVisit(StringValueCommand x, Context ctx) {
+ // "4~abcd
+ if (appendIdentity(x)) {
+ String value = x.getValue();
+ /*
+ * Emit this a a Pascal-style string, using an explicit length.
This
+ * avoids the need to escape the value.
+ */
+ appendTypedData(STRING_TYPE, String.valueOf(value.length()));
+ append(value);
+ }
+ }
+
+ @Override
+ public boolean visit(ArrayValueCommand x, Context ctx) {
+ /*
+ * Encoded as (leafType, dimensions, length, .... )
+ *
+ * Object[] foo = new Object[3];
+ *
+ * becomes
+ *
+ * [ObjectSeedname~1~3~@....~@....~@...~
+ *
+ * Object[][] foo = new Object[3][];
+ *
+ * becomes
+ *
+ * [ObjectSeedName~2~3~...three one-dim arrays...
+ */
+ if (appendIdentity(x)) {
+ int dims = 1;
+ Class<?> leaf = x.getComponentType();
+ while (leaf.getComponentType() != null) {
+ dims++;
+ leaf = leaf.getComponentType();
+ }
+
+ appendTypedData(ARRAY_TYPE, leaf.getName());
+ accept(new IntValueCommand(dims));
+ accept(new IntValueCommand(x.getComponentValues().size()));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean visit(InstantiateCommand x, Context ctx) {
+ // @TypeSeedName~3~... N-many setters ...
+ if (appendIdentity(x)) {
+ appendTypedData(OBJECT_TYPE, x.getTargetClass().getName());
+ accept(new IntValueCommand(x.getSetters().size()));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean visit(InvokeCustomFieldSerializerCommand x, Context
ctx) {
+ // !TypeSeedName~Number of objects written by CFS~...CFS objects...~
+ // Number of extra fields~...N-many setters...
+ if (appendIdentity(x)) {
+ appendTypedData(INVOKE_TYPE, x.getTargetClass().getName());
+ accept(new IntValueCommand(x.getValues().size()));
+ accept(x.getValues());
+ accept(new IntValueCommand(x.getSetters().size()));
+ accept(x.getSetters());
+ return false;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean visit(ReturnCommand x, Context ctx) {
+ // R4~...values...
+ appendTypedData(RETURN_TYPE, String.valueOf(x.getValues().size()));
+ return true;
+ }
+
+ @Override
+ public boolean visit(SetCommand x, Context ctx) {
+ /*
+ * In hosted-mode, the field's declaring class is written to the
stream to
+ * handle field shadowing. In web mode, this isn't necessary because
all
+ * field names are allocated in the same "object" scope.
+ *
+ * DeclaringClassName~FieldName~...value...
+ */
+ if (!GWT.isScript()) {
+ accept(new StringValueCommand(x.getFieldDeclClass().getName()));
+ }
+ accept(new StringValueCommand(x.getField()));
+ return true;
+ }
+
+ @Override
+ public boolean visit(ThrowCommand x, Context ctx) {
+ // T...value...
+ appendTypedData(THROW_TYPE, "");
+ return true;
+ }
+
+ private void append(String x) {
+ try {
+ buffer.append(EscapeUtil.escape(x)).append(RPC_SEPARATOR_CHAR);
+ } catch (IOException e) {
+ halt(e);
+ }
+ }
+
+ private boolean appendIdentity(ValueCommand x) {
+ Integer backRef = backRefs.get(x);
+ if (backRef != null) {
+ if (PRETTY) {
+ try {
+ buffer.append(NL_CHAR);
+ } catch (IOException e) {
+ halt(e);
+ }
+ }
+ append(BACKREF_TYPE + String.valueOf(backRef));
+ return false;
+ } else {
+ backRefs.put(x, backRefs.size());
+ return true;
+ }
+ }
+
+ private void appendTypedData(char type, String value) {
+ try {
+ if (PRETTY) {
+ buffer.append(NL_CHAR);
+ }
+ buffer.append(type).append(value).append(RPC_SEPARATOR_CHAR);
+ } catch (IOException e) {
+ halt(e);
+ }
+ }
+ }
+
+ /**
+ * Used for diagnostics.
+ */
+ static final boolean PRETTY = false;
+
+ public static final char ARRAY_TYPE = '[';
+ public static final char BACKREF_TYPE = '@';
+ public static final char BOOLEAN_TYPE = 'Z';
+ public static final char BYTE_TYPE = 'B';
+ public static final char CHAR_TYPE = 'C';
+ public static final char DOUBLE_TYPE = 'D';
+ public static final char ENUM_TYPE = 'E';
+ public static final char FLOAT_TYPE = 'F';
+ public static final char INT_TYPE = 'I';
+ public static final char INVOKE_TYPE = '!';
+ public static final char LONG_TYPE = 'J';
+ public static final char NL_CHAR = '\n';
+ public static final char OBJECT_TYPE = 'L';
+ public static final char RETURN_TYPE = 'R';
+ public static final char RPC_SEPARATOR_CHAR = '~';
+ public static final char SHORT_TYPE = 'S';
+ public static final char STRING_TYPE = '"';
+ public static final char THROW_TYPE = 'T';
+ public static final char VOID_TYPE = 'V';
+
+ private final Map<ValueCommand, Integer> backRefs = new
HashMap<ValueCommand, Integer>();
+
+ private final Appendable buffer;
+
+ public SimplePayloadSink(Appendable buffer) {
+ this.buffer = buffer;
+ }
+
+ @Override
+ public void accept(RpcCommand command) throws SerializationException {
+ (new Visitor()).accept(command);
+ }
+
+ @Override
+ public void finish() throws SerializationException {
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/client/impl/TypeOverrides.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/client/impl/TypeOverrides.java Mon
Jul 6 16:17:17 2009
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.client.impl;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+
+/**
+ * Contains serialization dispatch information for types that don't support
+ * simple serialization.
+ */
+public final class TypeOverrides extends JavaScriptObject {
+ /**
+ * An individual entry, which is a wrapper around the CFS's serialize
method.
+ */
+ public static final class SerializeFunction extends JavaScriptObject {
+ protected SerializeFunction() {
+ }
+
+ public native void serialize(SerializationStreamWriter writer,
+ Object instance) /*-{
+ this(writer, instance);
+ }-*/;
+ }
+
+ public static TypeOverrides create() {
+ return JavaScriptObject.createObject().cast();
+ }
+
+ protected TypeOverrides() {
+ }
+
+ public native String[] getExtraFields(String className) /*-{
+ return this['B' + className];
+ }-*/;
+
+ public native SerializeFunction getOverride(String className) /*-{
+ return this['A' + className];
+ }-*/;
+
+ public native boolean hasExtraFields(String className) /*-{
+ return !!this['B' + className];
+ }-*/;
+
+ public native void set(String className, SerializeFunction override) /*-{
+ this['A' + className] = override;
+ }-*/;
+
+ public void set(String className, SerializeFunction override,
+ String[] extraFields) {
+ set(className, override);
+ set(className, extraFields);
+ }
+
+ public native void set(String className, String[] extraFields) /*-{
+ this['B' + className] = extraFields;
+ }-*/;
+}
Added: trunk/user/src/com/google/gwt/rpc/linker/ClientOracleLinker.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/linker/ClientOracleLinker.java Mon
Jul 6 16:17:17 2009
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.linker;
+
+import com.google.gwt.core.ext.LinkerContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.linker.AbstractLinker;
+import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.core.ext.linker.CompilationResult;
+import com.google.gwt.core.ext.linker.LinkerOrder;
+import com.google.gwt.core.ext.linker.SymbolData;
+import com.google.gwt.core.ext.linker.SyntheticArtifact;
+import com.google.gwt.core.ext.linker.LinkerOrder.Order;
+import com.google.gwt.rpc.server.WebModeClientOracle.Builder;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Exports data required by server components for directly-evalable RPC.
+ */
+@LinkerOrder(Order.PRE)
+public class ClientOracleLinker extends AbstractLinker {
+
+ private static final String SUFFIX = ".gwt.rpc";
+
+ @Override
+ public String getDescription() {
+ return "deRPC linker";
+ }
+
+ @Override
+ public ArtifactSet link(TreeLogger logger, LinkerContext context,
+ ArtifactSet artifacts) throws UnableToCompleteException {
+ artifacts = new ArtifactSet(artifacts);
+
+ Map<String, List<String>> allSerializableFields = new HashMap<String,
List<String>>();
+
+ for (RpcDataArtifact data : artifacts.find(RpcDataArtifact.class)) {
+ allSerializableFields.putAll(data.getOperableFields());
+ }
+
+ for (CompilationResult result :
artifacts.find(CompilationResult.class)) {
+ Builder builder = new Builder();
+
+ for (Map.Entry<String, List<String>> entry :
allSerializableFields.entrySet()) {
+ builder.setSerializableFields(entry.getKey(), entry.getValue());
+ }
+
+ for (SymbolData symbolData : result.getSymbolMap()) {
+ builder.add(symbolData.getSymbolName(), symbolData.getJsniIdent(),
+ symbolData.getClassName(), symbolData.getMemberName(),
+ symbolData.getTypeId());
+ }
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ builder.getOracle().store(out);
+ } catch (IOException e) {
+ // Should generally not happen
+ logger.log(TreeLogger.ERROR, "Unable to store deRPC data", e);
+ throw new UnableToCompleteException();
+ }
+
+ SyntheticArtifact a = emitBytes(logger, out.toByteArray(),
+ result.getStrongName() + SUFFIX);
+ artifacts.add(a);
+ }
+
+ return artifacts;
+ }
+
+}
Added: trunk/user/src/com/google/gwt/rpc/linker/RpcDataArtifact.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/linker/RpcDataArtifact.java Mon Jul
6 16:17:17 2009
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.linker;
+
+import com.google.gwt.core.ext.linker.Artifact;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This artifact allows the RpcProxyCreator class to communicate with the
+ * ClientOracleLinker.
+ */
+public class RpcDataArtifact extends Artifact<RpcDataArtifact> {
+
+ private final String rpcServiceName;
+ private final Map<String, List<String>> fieldsByClassName = new
HashMap<String, List<String>>();
+
+ public RpcDataArtifact(String rpcServiceName) {
+ super(ClientOracleLinker.class);
+ this.rpcServiceName = rpcServiceName;
+ }
+
+ public Map<String, List<String>> getOperableFields() {
+ return fieldsByClassName;
+ }
+
+ @Override
+ public int hashCode() {
+ return rpcServiceName.hashCode();
+ }
+
+ public void setFields(String className, List<String> fields) {
+ fieldsByClassName.put(className, fields);
+ }
+
+ @Override
+ protected int compareToComparableArtifact(RpcDataArtifact o) {
+ return rpcServiceName.compareTo(o.rpcServiceName);
+ }
+
+ @Override
+ protected Class<RpcDataArtifact> getComparableArtifactType() {
+ return RpcDataArtifact.class;
+ }
+
+}
Added: trunk/user/src/com/google/gwt/rpc/rebind/RpcProxyCreator.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/rebind/RpcProxyCreator.java Mon Jul
6 16:17:17 2009
@@ -0,0 +1,432 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.rebind;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.impl.ArtificialRescue;
+import com.google.gwt.core.client.impl.Impl;
+import com.google.gwt.core.client.impl.ArtificialRescue.Rescue;
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JArrayType;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JEnumType;
+import com.google.gwt.core.ext.typeinfo.JField;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.javac.TypeOracleMediator;
+import com.google.gwt.dev.util.collect.Lists;
+import com.google.gwt.rpc.client.impl.CommandToStringWriter;
+import com.google.gwt.rpc.client.impl.RpcServiceProxy;
+import com.google.gwt.rpc.client.impl.TypeOverrides;
+import com.google.gwt.rpc.linker.RpcDataArtifact;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import com.google.gwt.user.client.rpc.impl.RemoteServiceProxy;
+import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.google.gwt.user.rebind.rpc.CustomFieldSerializerValidator;
+import com.google.gwt.user.rebind.rpc.ProxyCreator;
+import com.google.gwt.user.rebind.rpc.SerializableTypeOracle;
+import com.google.gwt.user.rebind.rpc.SerializableTypeOracleBuilder;
+import com.google.gwt.user.rebind.rpc.SerializationUtils;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Generates async proxy implementations using the RPC system.
+ */
+public class RpcProxyCreator extends ProxyCreator {
+ private String typeOverrideName;
+
+ public RpcProxyCreator(JClassType type) {
+ super(type);
+ }
+
+ @Override
+ protected String computeTypeNameExpression(JType paramType) {
+ if (paramType.isClass() != null) {
+ return "GWT.isScript() ? Impl.getNameOf(\"@"
+ + paramType.getQualifiedSourceName() + "\") : \""
+ + TypeOracleMediator.computeBinaryClassName(paramType) + "\"";
+ } else {
+ /*
+ * Consider the case of service methods that have interface
parameters;
+ * these types don't necessarily exist in the client code, so we
want to
+ * encode these type names in a way that can always be distinguished
from
+ * obfuscated type names.
+ */
+ return "\" " + TypeOracleMediator.computeBinaryClassName(paramType)
+ + "\"";
+ }
+ }
+
+ @Override
+ protected void generateProxyContructor(SourceWriter srcWriter) {
+ srcWriter.println("public " + getProxySimpleName() + "() {");
+ srcWriter.indent();
+ srcWriter.println("super(GWT.getModuleBaseURL(),");
+ srcWriter.indent();
+ srcWriter.println(getRemoteServiceRelativePath() + ",");
+ srcWriter.println("OVERRIDES);");
+ srcWriter.outdent();
+ srcWriter.outdent();
+ srcWriter.println("}");
+ }
+
+ /**
+ * Generate any fields required by the proxy.
+ */
+ @Override
+ protected void generateProxyFields(SourceWriter srcWriter,
+ SerializableTypeOracle serializableTypeOracle,
+ String serializationPolicyStrongName, String
remoteServiceInterfaceName) {
+ // Initialize a field with binary name of the remote service interface
+ srcWriter.println("private static final String
REMOTE_SERVICE_INTERFACE_NAME = "
+ + "\"" + remoteServiceInterfaceName + "\";");
+ srcWriter.println("private static final " +
TypeOverrides.class.getName()
+ + " OVERRIDES = GWT.isScript() ? " + typeOverrideName
+ + ".create() : null;");
+ srcWriter.println();
+ }
+
+ @Override
+ protected void generateTypeHandlers(TreeLogger logger, GeneratorContext
ctx,
+ SerializableTypeOracle serializationSto,
+ SerializableTypeOracle deserializationSto)
+ throws UnableToCompleteException {
+ String simpleName = serviceIntf.getSimpleSourceName()
+ + "_TypeOverridesFactory";
+ PrintWriter out = ctx.tryCreate(logger,
serviceIntf.getPackage().getName(),
+ simpleName);
+ if (out == null) {
+ return;
+ }
+
+ TypeOracle typeOracle = ctx.getTypeOracle();
+ JClassType objectType = typeOracle.getJavaLangObject();
+ Set<JType> classLiterals = new HashSet<JType>();
+ Map<JType, JMethod> serializerMethods = new HashMap<JType, JMethod>();
+ Map<JType, List<String>> fields = new HashMap<JType, List<String>>();
+
+ StringBuilder sb = writeArtificialRescues(typeOracle, serializationSto,
+ deserializationSto);
+
+ ClassSourceFileComposerFactory composerFactory = new
ClassSourceFileComposerFactory(
+ serviceIntf.getPackage().getName(), simpleName);
+ composerFactory.addImport(ArtificialRescue.class.getCanonicalName());
+ composerFactory.addImport(GWT.class.getCanonicalName());
+ composerFactory.addImport(Impl.class.getCanonicalName());
+ composerFactory.addImport(Rescue.class.getCanonicalName());
+ composerFactory.addImport(TypeOverrides.class.getCanonicalName());
+
composerFactory.addImport(TypeOverrides.SerializeFunction.class.getCanonicalName());
+
+ composerFactory.addAnnotationDeclaration(sb.toString());
+ SourceWriter sw = composerFactory.createSourceWriter(ctx, out);
+
+ sw.println("public static TypeOverrides create() {");
+ sw.indent();
+ sw.println("TypeOverrides toReturn = TypeOverrides.create();");
+ for (JType type : serializationSto.getSerializableTypes()) {
+ JClassType classType = type.isClass();
+ if (classType == null) {
+ continue;
+ }
+
+ /*
+ * Figure out which fields should be serialized and if there's a
CFS. This
+ * is done by crawling the supertype chain until we hit Object or a
type
+ * with a CFS.
+ */
+ boolean allFieldsAreSerializable = true;
+ List<String> fieldRefs = new ArrayList<String>();
+ JMethod serializerMethod = null;
+ do {
+ JClassType customSerializer =
SerializableTypeOracleBuilder.findCustomFieldSerializer(
+ typeOracle, classType);
+ serializerMethod = customSerializer == null ? null
+ : CustomFieldSerializerValidator.getSerializationMethod(
+ customSerializer, type.isClass());
+
+ if (serializerMethod != null) {
+ // Don't include any fields in the type
+ break;
+ }
+
+ JField[] serializableFields =
SerializationUtils.getSerializableFields(
+ typeOracle, classType);
+ allFieldsAreSerializable &= serializableFields.length ==
classType.getFields().length;
+ for (JField field : serializableFields) {
+ fieldRefs.add("@" +
field.getEnclosingType().getQualifiedSourceName()
+ + "::" + field.getName());
+ }
+ classType = classType.getSuperclass();
+ } while (classType != objectType);
+
+ if (allFieldsAreSerializable && serializerMethod == null) {
+ // We can just inspect the object at runtime; best for code size
+ continue;
+ }
+
+ if (serializerMethod != null || !fieldRefs.isEmpty()) {
+ classLiterals.add(type);
+
+ /*
+ * toReturn.set(class_foo_Bar().getName(), serializer_foo_Bar(),
+ * fields_foo_Bar());
+ */
+ String mangledTypeName =
type.getQualifiedSourceName().replace('.', '_');
+ sw.println("toReturn.set(class_" + mangledTypeName
+ "().getName()");
+ if (serializerMethod == null) {
+ } else {
+ serializerMethods.put(type, serializerMethod);
+ sw.indentln(",serializer_" + mangledTypeName + "()");
+ }
+ if (fieldRefs.isEmpty()) {
+ sw.indentln(");");
+ } else {
+ fields.put(type, fieldRefs);
+ sw.indentln(",fields_" + mangledTypeName + "());");
+ }
+ }
+ }
+
+ sw.println("return toReturn;");
+ sw.outdent();
+ sw.println("}");
+
+ for (JType classLiteral : classLiterals) {
+ sw.println("public static native Class class_"
+ + classLiteral.getQualifiedSourceName().replace('.', '_') + "()
/*-{");
+ sw.indentln("return @" + classLiteral.getQualifiedSourceName()
+ + "::class;");
+ sw.println("}-*/;");
+ sw.println();
+ }
+
+ for (Map.Entry<JType, JMethod> entry : serializerMethods.entrySet()) {
+ sw.println("public static native "
+ + TypeOverrides.SerializeFunction.class.getSimpleName()
+ + " serializer_"
+ + entry.getKey().getQualifiedSourceName().replace('.', '_')
+ + "() /*-{");
+ sw.indentln("return " + entry.getValue().getJsniSignature() + ";");
+ sw.println("}-*/;");
+ sw.println();
+ }
+
+ for (Map.Entry<JType, List<String>> entry : fields.entrySet()) {
+ sw.println("public static String[] fields_"
+ + entry.getKey().getQualifiedSourceName().replace('.', '_')
+ "() {");
+ sw.print("return new String[] {");
+ for (String fieldRef : entry.getValue()) {
+ sw.print("Impl.getNameOf(\"" + fieldRef + "\"),");
+ }
+ sw.println("};");
+ sw.println("}");
+ sw.println();
+ }
+
+ sw.commit(logger);
+
+ typeOverrideName = composerFactory.getCreatedClassName();
+ }
+
+ @Override
+ protected Class<? extends RemoteServiceProxy> getProxySupertype() {
+ return RpcServiceProxy.class;
+ }
+
+ @Override
+ protected Class<? extends SerializationStreamWriter>
getStreamWriterClass() {
+ return CommandToStringWriter.class;
+ }
+
+ @Override
+ protected String writeSerializationPolicyFile(TreeLogger logger,
+ GeneratorContext ctx, SerializableTypeOracle serializationSto,
+ SerializableTypeOracle deserializationSto)
+ throws UnableToCompleteException {
+
+ RpcDataArtifact data = new RpcDataArtifact(
+ serviceIntf.getQualifiedSourceName());
+
+ for (JType type : deserializationSto.getSerializableTypes()) {
+ if (!(type instanceof JClassType)) {
+ continue;
+ }
+
+ JField[] serializableFields =
SerializationUtils.getSerializableFields(
+ ctx.getTypeOracle(), (JClassType) type);
+
+ List<String> names = Lists.create();
+ for (int i = 0, j = serializableFields.length; i < j; i++) {
+ names = Lists.add(names, serializableFields[i].getName());
+ }
+
+ data.setFields(TypeOracleMediator.computeBinaryClassName(type),
names);
+ }
+
+ ctx.commitArtifact(logger, data);
+
+ return "unused";
+ }
+
+ private StringBuilder writeArtificialRescues(TypeOracle typeOracle,
+ SerializableTypeOracle serializationSto,
+ SerializableTypeOracle deserializationSto) {
+ Set<JType> serializableTypes = new HashSet<JType>();
+ Collections.addAll(serializableTypes,
+ serializationSto.getSerializableTypes());
+ Collections.addAll(serializableTypes,
+ deserializationSto.getSerializableTypes());
+
+ StringBuilder sb = new StringBuilder("@ArtificialRescue({");
+ for (JType serializableType : serializableTypes) {
+
+ JArrayType serializableArray = serializableType.isArray();
+ JClassType serializableClass = serializableType.isClass();
+ if (serializableArray != null) {
+ sb.append("\n@Rescue(className = \"");
+ if (serializableArray.getLeafType() instanceof JPrimitiveType) {
+ sb.append(serializableArray.getLeafType().getJNISignature());
+ for (int i = 0, j = serializableArray.getRank(); i < j; i++) {
+ sb.append("[]");
+ }
+ } else {
+ sb.append(serializableArray.getQualifiedSourceName());
+ }
+ sb.append("\",\n instantiable = true),");
+ } else if (serializableClass != null) {
+ writeSingleRescue(typeOracle, deserializationSto, sb,
serializableClass);
+ }
+ }
+ sb.append("})");
+ return sb;
+ }
+
+ /**
+ * Writes the rescue of a serializable type and its custom serialization
+ * logic.
+ */
+ private void writeSingleRescue(TypeOracle typeOracle,
+ SerializableTypeOracle deserializationOracle, StringBuilder sb,
+ JClassType serializableClass) {
+ boolean shouldDeserialize =
deserializationOracle.isSerializable(serializableClass);
+
+ // Pull the two custom serialization methods
+ JClassType customSerializer;
+ JMethod deserialize = null;
+ JMethod instantiate = null;
+
+ // An automatically-serializable subclass of a manually serialized
class
+ boolean hybridSerialization = false;
+
+ {
+ JClassType search = serializableClass;
+ do {
+ customSerializer =
SerializableTypeOracleBuilder.findCustomFieldSerializer(
+ typeOracle, search);
+
+ if (customSerializer != null) {
+ instantiate =
CustomFieldSerializerValidator.getInstantiationMethod(
+ customSerializer, search);
+
+ deserialize =
CustomFieldSerializerValidator.getDeserializationMethod(
+ customSerializer, search);
+
+ hybridSerialization = search != serializableClass;
+ break;
+ }
+
+ search = search.getSuperclass();
+ } while (search != null);
+ }
+
+ // The fields that should be preserved from being pruned
+ JField[] serializableFields;
+ JEnumType enumType = serializableClass.isEnum();
+ if (enumType != null) {
+ serializableFields = enumType.getFields();
+ } else {
+ serializableFields =
SerializationUtils.getSerializableFields(typeOracle,
+ serializableClass);
+ }
+
+ /*
+ * We need to rescue the constructor if there is no instantiate method
and
+ * there is a custom deserialize method.
+ */
+ boolean rescueConstructor = instantiate == null && deserialize != null;
+
+ /*
+ * There may be either no custom serializer or a custom serializer that
+ * doesn't define the instantiate method.
+ */
+ if (shouldDeserialize || rescueConstructor
+ || (customSerializer == null && serializableFields.length > 0)) {
+
+ /*
+ * @Rescue(className="package.Foo$Inner", instantiable=true,
fields={..},
+ * methods={..}),
+ */
+ sb.append("\n@Rescue(className = \"").append(
+ serializableClass.getQualifiedSourceName()).append("\"");
+
+ sb.append(",\n instantiable = ").append(shouldDeserialize);
+ sb.append(",\n fields = {");
+ if (customSerializer == null || hybridSerialization) {
+ for (JField field : serializableFields) {
+ sb.append("\"").append(field.getName()).append("\",");
+ }
+ }
+ sb.append("},\n methods = {");
+ if (rescueConstructor) {
+
sb.append("\"").append(serializableClass.getName().replace('.', '$')).append(
+ "()\"");
+ }
+ sb.append("}),");
+ }
+
+ // Rescue the custom serialization logic if any exists
+ if (customSerializer != null) {
+ sb.append("\n@Rescue(className = \"").append(
+ customSerializer.getQualifiedSourceName()).append("\",\n methods
= {");
+ if (instantiate != null) {
+ String jsniSignature = instantiate.getJsniSignature();
+ sb.append("\"").append(
+ jsniSignature.substring(jsniSignature.lastIndexOf(':') +
1)).append(
+ "\",");
+ }
+ if (deserialize != null) {
+ String jsniSignature = deserialize.getJsniSignature();
+ sb.append("\"").append(
+ jsniSignature.substring(jsniSignature.lastIndexOf(':') +
1)).append(
+ "\",");
+ }
+ sb.append("}),");
+ }
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/rebind/RpcServiceGenerator.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/rebind/RpcServiceGenerator.java Mon
Jul 6 16:17:17 2009
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.rebind;
+
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.user.rebind.rpc.ProxyCreator;
+import com.google.gwt.user.rebind.rpc.ServiceInterfaceProxyGenerator;
+
+/**
+ * Generator for RPC service interfaces.
+ */
+public class RpcServiceGenerator extends
+ ServiceInterfaceProxyGenerator {
+
+ @Override
+ protected ProxyCreator createProxyCreator(JClassType remoteService) {
+ return new RpcProxyCreator(remoteService);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/server/ClientOracle.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/server/ClientOracle.java Mon Jul 6
16:17:17 2009
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.server;
+
+import com.google.gwt.rpc.client.ast.CommandSink;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+
+/**
+ * Encapsulates information about a remote client. This type is not
intended to
+ * be implemented by end-users although the behavior of a concrete
+ * implementation may be modified via the {@link DelegatingClientOracle}
type.
+ */
+public abstract class ClientOracle {
+
+ /**
+ * Not a generally-extensible class.
+ */
+ ClientOracle() {
+ }
+
+ /**
+ * Create a CommandSink that can encode a payload for the client.
+ *
+ * @param out the OutputStream to which the output will be written
+ * @return a CommandSink
+ * @throws IOException if the CommandSink cannot write to the
OutputStream
+ */
+ public abstract CommandSink createCommandSink(OutputStream out)
+ throws IOException;
+
+ /**
+ * Returns an identifier that does not conflict with any symbols defined
in
+ * the client. This method does not accumulate any state.
+ */
+ public abstract String createUnusedIdent(String ident);
+
+ /**
+ * Given a base type and the unobfuscated field name, find the
obfuscated name
+ * for the field in the client. This will search superclasses as well
for the
+ * first matching field.
+ */
+ public abstract String getFieldId(Class<?> clazz, String fieldName);
+
+ /**
+ * Return the field name for a given enum value.
+ */
+ public abstract String getFieldId(Enum<?> value);
+
+ /**
+ * This is similar to {@link #getFieldId(Class, String)} but does not
search
+ * supertypes. It is intended to be used to access "magic" GWT types.
+ */
+ public abstract String getFieldId(String className, String fieldName);
+
+ /**
+ * Return the name of a field from a client-side id. This will search
+ * superclasses for the first instance of the named field.
+ *
+ * @returns The field's declaring class and the name of the field
+ */
+ public abstract Pair<Class<?>, String> getFieldName(Class<?> clazz,
+ String fieldId);
+
+ /**
+ * Returns the name of the top-level function which implements the named
+ * method that takes the exact arguments specified. This will search in
the
+ * given class as well as its supertypes.
+ */
+ public abstract String getMethodId(Class<?> clazz, String methodName,
+ Class<?>... args);
+
+ /**
+ * This is similar to {@link #getMethodId(Class, String, Class...)} but
does
+ * not search supertypes. It is intended to be used to access "magic" GWT
+ * types.
+ */
+ public abstract String getMethodId(String className, String methodName,
+ String... jsniArgTypes);
+
+ /**
+ * Returns the fields of a given class that should be serialized. This
method
+ * does not crawl supertypes.
+ */
+ public abstract Field[] getOperableFields(Class<?> clazz);
+
+ /**
+ * Returns the name of the top-level function that is used as the seed
+ * function for a given type.
+ */
+ public abstract String getSeedName(Class<?> clazz);
+
+ /**
+ * Returns the assigned typeId of a given type or 0.
+ */
+ public abstract int getTypeId(Class<?> clazz);
+
+ /**
+ * Returns the deobfuscated name of a type based on the name of the
type's
+ * seed function.
+ */
+ public abstract String getTypeName(String seedName);
+
+ /**
+ * Indicates whether or not the remote client is running as compiled
script.
+ * This may be used to optimize the payload based on assumptions that
can be
+ * mode about web-mode or hosted-mode clients.
+ */
+ public abstract boolean isScript();
+}
Added:
trunk/user/src/com/google/gwt/rpc/server/CommandSerializationUtil.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/server/CommandSerializationUtil.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,477 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.server;
+
+import com.google.gwt.rpc.client.ast.BooleanValueCommand;
+import com.google.gwt.rpc.client.ast.ByteValueCommand;
+import com.google.gwt.rpc.client.ast.CharValueCommand;
+import com.google.gwt.rpc.client.ast.DoubleValueCommand;
+import com.google.gwt.rpc.client.ast.FloatValueCommand;
+import com.google.gwt.rpc.client.ast.IntValueCommand;
+import com.google.gwt.rpc.client.ast.LongValueCommand;
+import com.google.gwt.rpc.client.ast.ShortValueCommand;
+import com.google.gwt.rpc.client.ast.StringValueCommand;
+import com.google.gwt.rpc.client.ast.ValueCommand;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+import sun.misc.Unsafe;
+
+/**
+ * Contains common utility code.
+ */
+public class CommandSerializationUtil {
+
+ /**
+ * Defines methods for getting and setting fields.
+ */
+ public interface Accessor {
+ boolean canMakeValueCommand();
+
+ /**
+ * Indicates if set can be called with a value of the given type.
+ */
+ boolean canSet(Class<?> clazz);
+
+ Object get(Object instance, Field f);
+
+ Class<?> getTargetType();
+
+ ValueCommand makeValueCommand(Object value);
+
+ Object readNext(SerializationStreamReader reader)
+ throws SerializationException;
+
+ void set(Object instance, Field f, Object value);
+
+ void set(Object array, int index, Object value);
+ }
+
+ /**
+ * Defines type-specific methods of getting and setting fields.
+ */
+ private static enum TypeAccessor implements Accessor {
+ BOOL {
+ @Override
+ public boolean canSet(Class<?> clazz) {
+ return Boolean.class.isAssignableFrom(clazz);
+ }
+
+ @Override
+ public Object get(Object instance, long offset) {
+ return theUnsafe.getBoolean(instance, offset);
+ }
+
+ @Override
+ public Object getDefaultValue() {
+ return false;
+ }
+
+ @Override
+ public Class<?> getTargetType() {
+ return boolean.class;
+ }
+
+ @Override
+ public ValueCommand makeValueCommand(Object value) {
+ return new BooleanValueCommand((Boolean) value);
+ }
+
+ @Override
+ public Object readNext(SerializationStreamReader reader)
+ throws SerializationException {
+ return reader.readBoolean();
+ }
+
+ @Override
+ public void set(Object instance, long offset, Object value) {
+ theUnsafe.putBoolean(instance, offset, ((Boolean) value));
+ }
+ },
+ BYTE {
+ @Override
+ public Object get(Object instance, long offset) {
+ return theUnsafe.getByte(instance, offset);
+ }
+
+ @Override
+ public Class<?> getTargetType() {
+ return byte.class;
+ }
+
+ @Override
+ public ValueCommand makeValueCommand(Object value) {
+ return new ByteValueCommand((Byte) value);
+ }
+
+ @Override
+ public Object readNext(SerializationStreamReader reader)
+ throws SerializationException {
+ return reader.readByte();
+ }
+
+ @Override
+ public void set(Object instance, long offset, Object value) {
+ theUnsafe.putByte(instance, offset, ((Number) value).byteValue());
+ }
+ },
+ CHAR {
+ @Override
+ public Object get(Object instance, long offset) {
+ return theUnsafe.getChar(instance, offset);
+ }
+
+ @Override
+ public Class<?> getTargetType() {
+ return char.class;
+ }
+
+ @Override
+ public ValueCommand makeValueCommand(Object value) {
+ return new CharValueCommand((Character) value);
+ }
+
+ @Override
+ public Object readNext(SerializationStreamReader reader)
+ throws SerializationException {
+ return reader.readChar();
+ }
+
+ @Override
+ public void set(Object instance, long offset, Object value) {
+ char c = (value instanceof Number) ? (char) ((Number)
value).intValue()
+ : (Character) value;
+ theUnsafe.putChar(instance, offset, c);
+ }
+ },
+ DOUBLE {
+ @Override
+ public Object get(Object instance, long offset) {
+ return theUnsafe.getDouble(instance, offset);
+ }
+
+ @Override
+ public Class<?> getTargetType() {
+ return double.class;
+ }
+
+ @Override
+ public ValueCommand makeValueCommand(Object value) {
+ return new DoubleValueCommand((Double) value);
+ }
+
+ @Override
+ public Object readNext(SerializationStreamReader reader)
+ throws SerializationException {
+ return reader.readDouble();
+ }
+
+ @Override
+ public void set(Object instance, long offset, Object value) {
+ theUnsafe.putDouble(instance, offset, ((Number)
value).doubleValue());
+ }
+ },
+ FLOAT {
+ @Override
+ public Object get(Object instance, long offset) {
+ return theUnsafe.getFloat(instance, offset);
+ }
+
+ @Override
+ public Class<?> getTargetType() {
+ return float.class;
+ }
+
+ @Override
+ public ValueCommand makeValueCommand(Object value) {
+ return new FloatValueCommand((Float) value);
+ }
+
+ @Override
+ public Object readNext(SerializationStreamReader reader)
+ throws SerializationException {
+ return reader.readFloat();
+ }
+
+ @Override
+ public void set(Object instance, long offset, Object value) {
+ theUnsafe.putFloat(instance, offset, ((Number)
value).floatValue());
+ }
+ },
+ INT {
+ @Override
+ public Object get(Object instance, long offset) {
+ return theUnsafe.getInt(instance, offset);
+ }
+
+ @Override
+ public Class<?> getTargetType() {
+ return int.class;
+ }
+
+ @Override
+ public ValueCommand makeValueCommand(Object value) {
+ return new IntValueCommand(((Number) value).intValue());
+ }
+
+ @Override
+ public Object readNext(SerializationStreamReader reader)
+ throws SerializationException {
+ return reader.readInt();
+ }
+
+ @Override
+ public void set(Object instance, long offset, Object value) {
+ theUnsafe.putInt(instance, offset, ((Number) value).intValue());
+ }
+ },
+ LONG {
+ @Override
+ public Object get(Object instance, long offset) {
+ return theUnsafe.getLong(instance, offset);
+ }
+
+ @Override
+ public Class<?> getTargetType() {
+ return long.class;
+ }
+
+ @Override
+ public ValueCommand makeValueCommand(Object value) {
+ return new LongValueCommand((Long) value);
+ }
+
+ @Override
+ public Object readNext(SerializationStreamReader reader)
+ throws SerializationException {
+ return reader.readLong();
+ }
+
+ @Override
+ public void set(Object instance, long offset, Object value) {
+ theUnsafe.putLong(instance, offset, ((Number) value).longValue());
+ }
+ },
+ OBJECT {
+ @Override
+ public boolean canMakeValueCommand() {
+ return false;
+ }
+
+ @Override
+ public boolean canSet(Class<?> clazz) {
+ return Object.class.isAssignableFrom(clazz);
+ }
+
+ @Override
+ public Object get(Object instance, long offset) {
+ return theUnsafe.getObject(instance, offset);
+ }
+
+ @Override
+ public Object getDefaultValue() {
+ return null;
+ }
+
+ @Override
+ public Class<?> getTargetType() {
+ return Object.class;
+ }
+
+ @Override
+ public ValueCommand makeValueCommand(Object value) {
+ throw new RuntimeException("Cannot call makeValueCommand for
Objects");
+ }
+
+ @Override
+ public Object readNext(SerializationStreamReader reader)
+ throws SerializationException {
+ return reader.readObject();
+ }
+
+ @Override
+ public void set(Object instance, long offset, Object value) {
+ theUnsafe.putObject(instance, offset, value);
+ }
+ },
+ SHORT {
+ @Override
+ public Object get(Object instance, long offset) {
+ return theUnsafe.getShort(instance, offset);
+ }
+
+ @Override
+ public Class<?> getTargetType() {
+ return short.class;
+ }
+
+ @Override
+ public ValueCommand makeValueCommand(Object value) {
+ return new ShortValueCommand((Short) value);
+ }
+
+ @Override
+ public Object readNext(SerializationStreamReader reader)
+ throws SerializationException {
+ return reader.readShort();
+ }
+
+ @Override
+ public void set(Object instance, long offset, Object value) {
+ theUnsafe.putShort(instance, offset, ((Number)
value).shortValue());
+ }
+ },
+ STRING {
+ @Override
+ public boolean canSet(Class<?> clazz) {
+ return true;
+ }
+
+ @Override
+ public Object get(Object instance, long offset) {
+ return theUnsafe.getObject(instance, offset);
+ }
+
+ @Override
+ public Object getDefaultValue() {
+ return null;
+ }
+
+ @Override
+ public Class<?> getTargetType() {
+ return String.class;
+ }
+
+ @Override
+ public ValueCommand makeValueCommand(Object value) {
+ return new StringValueCommand((String) value);
+ }
+
+ @Override
+ public Object readNext(SerializationStreamReader reader)
+ throws SerializationException {
+ return reader.readObject();
+ }
+
+ @Override
+ public void set(Object instance, long offset, Object value) {
+ theUnsafe.putObject(instance, offset, value == null ? null
+ : value.toString());
+ }
+ };
+
+ private final Class<?> arrayType = Array.newInstance(getTargetType(),
0).getClass();
+ private final long arrayBase = theUnsafe.arrayBaseOffset(arrayType);
+ private final long arrayIndexScale =
theUnsafe.arrayIndexScale(arrayType);
+
+ public boolean canMakeValueCommand() {
+ return true;
+ }
+
+ public boolean canSet(Class<?> clazz) {
+ return Number.class.isAssignableFrom(clazz);
+ }
+
+ public Object get(Object instance, Field f) {
+ long offset = objectFieldOffset(f);
+ return get(instance, offset);
+ }
+
+ public abstract Object get(Object instance, long offset);
+
+ public Object getDefaultValue() {
+ return 0;
+ }
+
+ public abstract Class<?> getTargetType();
+
+ public abstract ValueCommand makeValueCommand(Object value);
+
+ public abstract Object readNext(SerializationStreamReader reader)
+ throws SerializationException;
+
+ public void set(Object instance, Field f, Object value) {
+ long offset = objectFieldOffset(f);
+ set(instance, offset, value == null ? getDefaultValue() : value);
+ }
+
+ public void set(Object array, int index, Object value) {
+ set(array, arrayBase + arrayIndexScale * index, value);
+ }
+
+ public abstract void set(Object instance, long offset, Object value);
+ }
+
+ private static final Map<Class<?>, Accessor> ACCESSORS = new
IdentityHashMap<Class<?>, Accessor>();
+ private static final Unsafe theUnsafe;
+
+ static {
+ Exception ex = null;
+ Unsafe localUnsafe = null;
+ try {
+ Field f = Unsafe.class.getDeclaredField("theUnsafe");
+ f.setAccessible(true);
+ localUnsafe = (Unsafe) f.get(null);
+ } catch (SecurityException e) {
+ ex = e;
+ } catch (NoSuchFieldException e) {
+ ex = e;
+ } catch (IllegalArgumentException e) {
+ ex = e;
+ } catch (IllegalAccessException e) {
+ ex = e;
+ }
+ if (ex != null) {
+ throw new RuntimeException("Unable to get Unsafe instance", ex);
+ } else {
+ theUnsafe = localUnsafe;
+ }
+
+ for (TypeAccessor setter : TypeAccessor.values()) {
+ ACCESSORS.put(setter.getTargetType(), setter);
+ }
+ }
+
+ public static Accessor getAccessor(Class<?> clazz) {
+ Accessor toReturn = ACCESSORS.get(clazz);
+ if (toReturn == null) {
+ toReturn = TypeAccessor.OBJECT;
+ }
+ return toReturn;
+ }
+
+ /**
+ * TODO: In the future it may be preferable to use a custom ClassLoader
to
+ * inject a constructor that will initialize all of the final fields
that we
+ * care about.
+ */
+ static <T> T allocateInstance(Class<T> clazz) throws
InstantiationException {
+ Object obj = theUnsafe.allocateInstance(clazz);
+ return clazz.cast(obj);
+ }
+
+ private static long objectFieldOffset(Field f) {
+ return theUnsafe.objectFieldOffset(f);
+ }
+
+ private CommandSerializationUtil() {
+ }
+}
Added:
trunk/user/src/com/google/gwt/rpc/server/CommandServerSerializationStreamReader.java
==============================================================================
--- (empty file)
+++
trunk/user/src/com/google/gwt/rpc/server/CommandServerSerializationStreamReader.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,378 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.server;
+
+import com.google.gwt.rpc.client.ast.ArrayValueCommand;
+import com.google.gwt.rpc.client.ast.BooleanValueCommand;
+import com.google.gwt.rpc.client.ast.ByteValueCommand;
+import com.google.gwt.rpc.client.ast.CharValueCommand;
+import com.google.gwt.rpc.client.ast.DoubleValueCommand;
+import com.google.gwt.rpc.client.ast.EnumValueCommand;
+import com.google.gwt.rpc.client.ast.FloatValueCommand;
+import com.google.gwt.rpc.client.ast.IdentityValueCommand;
+import com.google.gwt.rpc.client.ast.InstantiateCommand;
+import com.google.gwt.rpc.client.ast.IntValueCommand;
+import com.google.gwt.rpc.client.ast.InvokeCustomFieldSerializerCommand;
+import com.google.gwt.rpc.client.ast.LongValueCommand;
+import com.google.gwt.rpc.client.ast.NullValueCommand;
+import com.google.gwt.rpc.client.ast.RpcCommandVisitor;
+import com.google.gwt.rpc.client.ast.ScalarValueCommand;
+import com.google.gwt.rpc.client.ast.SetCommand;
+import com.google.gwt.rpc.client.ast.ShortValueCommand;
+import com.google.gwt.rpc.client.ast.StringValueCommand;
+import com.google.gwt.rpc.client.ast.ValueCommand;
+import com.google.gwt.rpc.server.CommandSerializationUtil.Accessor;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+/**
+ * This class will use ValueCommands to reconstitute objects.
+ */
+public class CommandServerSerializationStreamReader implements
+ SerializationStreamReader {
+
+ class Visitor extends RpcCommandVisitor {
+
+ private final Stack<Object> values = new Stack<Object>();
+
+ @Override
+ public void endVisit(BooleanValueCommand x, Context ctx) {
+ pushScalar(x);
+ }
+
+ @Override
+ public void endVisit(ByteValueCommand x, Context ctx) {
+ pushScalar(x);
+ }
+
+ @Override
+ public void endVisit(CharValueCommand x, Context ctx) {
+ pushScalar(x);
+ }
+
+ @Override
+ public void endVisit(DoubleValueCommand x, Context ctx) {
+ pushScalar(x);
+ }
+
+ @Override
+ public void endVisit(EnumValueCommand x, Context ctx) {
+ push(x, x.getValue());
+ }
+
+ @Override
+ public void endVisit(FloatValueCommand x, Context ctx) {
+ pushScalar(x);
+ }
+
+ @Override
+ public void endVisit(IntValueCommand x, Context ctx) {
+ pushScalar(x);
+ }
+
+ @Override
+ public void endVisit(LongValueCommand x, Context ctx) {
+ pushScalar(x);
+ }
+
+ @Override
+ public void endVisit(NullValueCommand x, Context ctx) {
+ pushScalar(x);
+ }
+
+ @Override
+ public void endVisit(SetCommand x, Context ctx) {
+ Exception ex;
+ try {
+ Field f = x.getFieldDeclClass().getDeclaredField(x.getField());
+ Object value = values.pop();
+ Object instance = values.peek();
+ assert value == null
+ || CommandSerializationUtil.getAccessor(f.getType()).canSet(
+ value.getClass()) : "Cannot assign a "
+ + value.getClass().getName() + " into " +
f.getType().getName();
+
+ CommandSerializationUtil.getAccessor(f.getType()).set(instance, f,
+ value);
+ return;
+ } catch (SecurityException e) {
+ ex = e;
+ } catch (NoSuchFieldException e) {
+ ex = e;
+ }
+ halt(new SerializationException("Unable to set field value", ex));
+ }
+
+ @Override
+ public void endVisit(ShortValueCommand x, Context ctx) {
+ pushScalar(x);
+ }
+
+ @Override
+ public void endVisit(StringValueCommand x, Context ctx) {
+ pushScalar(x);
+ }
+
+ @Override
+ public boolean visit(ArrayValueCommand x, Context ctx) {
+ if (maybePushBackRef(x)) {
+ Object array = Array.newInstance(x.getComponentType(),
+ x.getComponentValues().size());
+ push(x, array);
+
+ int size = x.getComponentValues().size();
+ Accessor a =
CommandSerializationUtil.getAccessor(x.getComponentType());
+ for (int i = 0; i < size; i++) {
+ accept(x.getComponentValues().get(i));
+ a.set(array, i, values.pop());
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean visit(InstantiateCommand x, Context ctx) {
+ if (maybePushBackRef(x)) {
+ Object instance;
+ try {
+ instance =
CommandSerializationUtil.allocateInstance(x.getTargetClass());
+ push(x, instance);
+ return true;
+ } catch (InstantiationException e) {
+ halt(new SerializationException("Unable to create instance", e));
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean visit(InvokeCustomFieldSerializerCommand x, Context
ctx) {
+ if (maybePushBackRef(x)) {
+
+ CommandServerSerializationStreamReader subReader = new
CommandServerSerializationStreamReader();
+ subReader.prepareToRead(x.getValues());
+
+ Class<?> serializerClass = x.getSerializerClass();
+ assert serializerClass != null;
+
+ Method instantiate = null;
+ Method deserialize = null;
+ for (Method m : serializerClass.getMethods()) {
+ if ("instantiate".equals(m.getName())) {
+ instantiate = m;
+ } else if ("deserialize".equals(m.getName())) {
+ deserialize = m;
+ }
+
+ if (instantiate != null && deserialize != null) {
+ break;
+ }
+ }
+
+ assert deserialize != null : "No deserialize method in "
+ + serializerClass.getName();
+
+ Object instance = null;
+ if (instantiate != null) {
+ assert
Modifier.isStatic(instantiate.getModifiers()) : "instantiate method in "
+ + serializerClass.getName() + " must be static";
+ try {
+ instance = instantiate.invoke(null, subReader);
+ } catch (IllegalArgumentException e) {
+ halt(new SerializationException("Unable to create instance",
e));
+ } catch (IllegalAccessException e) {
+ halt(new SerializationException("Unable to create instance",
e));
+ } catch (InvocationTargetException e) {
+ halt(new SerializationException("Unable to create instance",
e));
+ }
+ } else {
+ try {
+ instance = x.getTargetClass().newInstance();
+ } catch (InstantiationException e) {
+ halt(new SerializationException("Unable to create instance",
e));
+ } catch (IllegalAccessException e) {
+ halt(new SerializationException("Unable to create instance",
e));
+ }
+ }
+
+ assert instance != null : "Did not create instance";
+ push(x, instance);
+
+ // Process any additional fields
+ accept(x.getSetters());
+
+ try {
+ deserialize.invoke(null, subReader, instance);
+ } catch (IllegalArgumentException e) {
+ halt(new SerializationException("Unable to deserialize
instance", e));
+ } catch (IllegalAccessException e) {
+ halt(new SerializationException("Unable to deserialize
instance", e));
+ } catch (InvocationTargetException e) {
+ halt(new SerializationException("Unable to deserialize
instance", e));
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if the command must be processed.
+ */
+ private boolean maybePushBackRef(IdentityValueCommand x) {
+ Object instance = backRefs.get(x);
+ if (instance == null) {
+ return true;
+ } else {
+ values.push(instance);
+ return false;
+ }
+ }
+
+ private void push(IdentityValueCommand x, Object value) {
+ assert !backRefs.containsKey(x) : "Trying to redefine a backref";
+ backRefs.put(x, value);
+ values.push(value);
+ }
+
+ private void pushScalar(ScalarValueCommand x) {
+ values.push(x.getValue());
+ }
+ }
+
+ Map<IdentityValueCommand, Object> backRefs = new
HashMap<IdentityValueCommand, Object>();
+ Iterator<ValueCommand> values;
+
+ public void prepareToRead(List<ValueCommand> commands) {
+ values = commands.iterator();
+ assert values.hasNext() : "No commands";
+ }
+
+ public boolean readBoolean() throws SerializationException {
+ return readNumberCommand(BooleanValueCommand.class).getValue();
+ }
+
+ public byte readByte() throws SerializationException {
+ return readNumberCommand(ByteValueCommand.class).getValue();
+ }
+
+ public char readChar() throws SerializationException {
+ return readNumberCommand(CharValueCommand.class).getValue();
+ }
+
+ public double readDouble() throws SerializationException {
+ return readNumberCommand(DoubleValueCommand.class).getValue();
+ }
+
+ public float readFloat() throws SerializationException {
+ return readNumberCommand(FloatValueCommand.class).getValue();
+ }
+
+ public int readInt() throws SerializationException {
+ return readNumberCommand(IntValueCommand.class).getValue();
+ }
+
+ public long readLong() throws SerializationException {
+ return readNumberCommand(LongValueCommand.class).getValue();
+ }
+
+ public Object readObject() throws SerializationException {
+ ValueCommand command = readNextCommand(ValueCommand.class);
+ Visitor v = new Visitor();
+ v.accept(command);
+ return v.values.pop();
+ }
+
+ public short readShort() throws SerializationException {
+ return readNumberCommand(ShortValueCommand.class).getValue();
+ }
+
+ public String readString() throws SerializationException {
+ return readNextCommand(StringValueCommand.class).getValue();
+ }
+
+ private <T extends ValueCommand> T readNextCommand(Class<T> clazz)
+ throws SerializationException {
+ if (!values.hasNext()) {
+ throw new SerializationException("Reached end of stream");
+ }
+ ValueCommand next = values.next();
+ if (!clazz.isInstance(next)) {
+ throw new SerializationException("Cannot assign "
+ + next.getClass().getName() + " to " + clazz.getName());
+ }
+ return clazz.cast(next);
+ }
+
+ /**
+ * Will perform narrowing conversions from double type to other numeric
types.
+ */
+ private <T extends ValueCommand> T readNumberCommand(Class<T> clazz)
+ throws SerializationException {
+ if (!values.hasNext()) {
+ throw new SerializationException("Reached end of stream");
+ }
+ ValueCommand next = values.next();
+
+ if (clazz.isInstance(next)) {
+ return clazz.cast(next);
+ } else if (next instanceof LongValueCommand) {
+ if (!clazz.isInstance(next)) {
+ throw new SerializationException("Cannot assign "
+ + next.getClass().getName() + " to " + clazz.getName());
+ }
+ return clazz.cast(next);
+ } else if (next instanceof DoubleValueCommand) {
+ Exception ex;
+ try {
+ Constructor<T> c = clazz.getConstructor(double.class);
+ return c.newInstance(((DoubleValueCommand)
next).getValue().doubleValue());
+ } catch (SecurityException e) {
+ throw new SerializationException("Cannot construct ValueCommand
type",
+ e);
+ } catch (NoSuchMethodException e) {
+ throw new SerializationException("Connot initialize a "
+ + clazz.getName() + " from a DoubleValueCommand", e);
+ } catch (IllegalArgumentException e) {
+ ex = e;
+ } catch (InstantiationException e) {
+ ex = e;
+ } catch (IllegalAccessException e) {
+ ex = e;
+ } catch (InvocationTargetException e) {
+ ex = e;
+ }
+ throw new SerializationException("Cannot create ValueCommand", ex);
+ } else {
+ throw new SerializationException(
+ "Cannot create a numeric ValueCommand from a "
+ + next.getClass().getName());
+ }
+ }
+}
Added:
trunk/user/src/com/google/gwt/rpc/server/CommandServerSerializationStreamWriter.java
==============================================================================
--- (empty file)
+++
trunk/user/src/com/google/gwt/rpc/server/CommandServerSerializationStreamWriter.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.server;
+
+import com.google.gwt.rpc.client.ast.ArrayValueCommand;
+import com.google.gwt.rpc.client.ast.CommandSink;
+import com.google.gwt.rpc.client.ast.EnumValueCommand;
+import com.google.gwt.rpc.client.ast.HasSetters;
+import com.google.gwt.rpc.client.ast.IdentityValueCommand;
+import com.google.gwt.rpc.client.ast.InstantiateCommand;
+import com.google.gwt.rpc.client.ast.InvokeCustomFieldSerializerCommand;
+import com.google.gwt.rpc.client.ast.NullValueCommand;
+import com.google.gwt.rpc.client.ast.ValueCommand;
+import com.google.gwt.rpc.client.impl.CommandSerializationStreamWriterBase;
+import com.google.gwt.rpc.client.impl.HasValuesCommandSink;
+import com.google.gwt.rpc.server.CommandSerializationUtil.Accessor;
+import com.google.gwt.user.client.rpc.IsSerializable;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.server.rpc.impl.SerializabilityUtil;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ * A server-side implementation of SerializationStreamWriter that creates a
+ * command stream.
+ */
+public class CommandServerSerializationStreamWriter extends
+ CommandSerializationStreamWriterBase {
+
+ private final ClientOracle clientOracle;
+ private final Map<Object, IdentityValueCommand> identityMap = new
IdentityHashMap<Object, IdentityValueCommand>();
+
+ public CommandServerSerializationStreamWriter(CommandSink sink) {
+ this(new HostedModeClientOracle(), sink);
+ }
+
+ public CommandServerSerializationStreamWriter(ClientOracle oracle,
+ CommandSink sink) {
+ super(sink);
+ this.clientOracle = oracle;
+ }
+
+ /**
+ * Type is passed in to handle primitive types.
+ */
+ @Override
+ protected ValueCommand makeValue(Class<?> type, Object value)
+ throws SerializationException {
+ if (value == null) {
+ return NullValueCommand.INSTANCE;
+ }
+
+ Accessor accessor;
+
+ if (identityMap.containsKey(value)) {
+ return identityMap.get(value);
+
+ } else if ((accessor =
CommandSerializationUtil.getAccessor(type)).canMakeValueCommand()) {
+ return accessor.makeValueCommand(value);
+
+ } else if (type.isArray()) {
+ return makeArray(type, value);
+
+ } else if (Enum.class.isAssignableFrom(type)) {
+ return makeEnum(value);
+
+ } else {
+ return makeObject(type, value);
+ }
+ }
+
+ private ArrayValueCommand makeArray(Class<?> type, Object value)
+ throws SerializationException {
+ ArrayValueCommand toReturn = new
ArrayValueCommand(type.getComponentType());
+ for (int i = 0, j = Array.getLength(value); i < j; i++) {
+ Object arrayValue = Array.get(value, i);
+ if (arrayValue == null) {
+ toReturn.add(NullValueCommand.INSTANCE);
+ } else {
+ Class<? extends Object> valueType =
type.getComponentType().isPrimitive()
+ ? type.getComponentType() : arrayValue.getClass();
+ toReturn.add(makeValue(valueType, arrayValue));
+ }
+ }
+ identityMap.put(value, toReturn);
+ return toReturn;
+ }
+
+ private ValueCommand makeEnum(Object value) {
+ EnumValueCommand toReturn = new EnumValueCommand();
+ toReturn.setValue((Enum<?>) value);
+ return toReturn;
+ }
+
+ /*
+ * TODO: Profiling shows that the reflection and conditional logic in
this
+ * method is a hotspot. This could be remedied by generating synthetic
+ * InstantiateCommand types that initialize themselves.
+ */
+ private IdentityValueCommand makeObject(Class<?> type, Object value)
+ throws SerializationException {
+
+ if (type.isAnonymousClass() || type.isLocalClass()) {
+ throw new SerializationException(
+ "Cannot serialize anonymous or local classes");
+ }
+
+ Class<?> manualType = type;
+ Class<?> customSerializer;
+ do {
+ customSerializer =
SerializabilityUtil.hasCustomFieldSerializer(manualType);
+ if (customSerializer != null) {
+ break;
+ }
+ manualType = manualType.getSuperclass();
+ } while (manualType != null);
+
+ IdentityValueCommand ins;
+ if (customSerializer != null) {
+ ins = serializeWithCustomSerializer(customSerializer, value, type,
+ manualType);
+ } else {
+ ins = new InstantiateCommand(type);
+ identityMap.put(value, ins);
+ }
+
+ /*
+ * If we're looking at a subclass of a manually-serialized type, the
+ * subclass must be tagged as serializable in order to qualify for
+ * serialization.
+ */
+ if (type != manualType) {
+ if (!Serializable.class.isAssignableFrom(type)
+ && !IsSerializable.class.isAssignableFrom(type)) {
+ throw new SerializationException(type.getName()
+ + " is not a serializable type");
+ }
+ }
+
+ while (type != manualType) {
+ Field[] serializableFields = clientOracle.getOperableFields(type);
+ for (Field declField : serializableFields) {
+ assert (declField != null);
+
+ Accessor accessor =
CommandSerializationUtil.getAccessor(declField.getType());
+ ValueCommand valueCommand;
+ Object fieldValue = accessor.get(value, declField);
+ if (fieldValue == null) {
+ valueCommand = NullValueCommand.INSTANCE;
+ } else {
+ Class<? extends Object> fieldType =
declField.getType().isPrimitive()
+ ? declField.getType() : fieldValue.getClass();
+ valueCommand = makeValue(fieldType, fieldValue);
+ }
+
+ ((HasSetters) ins).set(declField.getDeclaringClass(),
+ declField.getName(), valueCommand);
+ }
+ type = type.getSuperclass();
+ }
+ return ins;
+ }
+
+ private InvokeCustomFieldSerializerCommand serializeWithCustomSerializer(
+ Class<?> customSerializer, Object instance, Class<?> instanceClass,
+ Class<?> manuallySerializedType) throws SerializationException {
+ assert !instanceClass.isArray();
+
+ Exception ex;
+ try {
+ /*
+ * NB: Class.getMethod() wants exact formal types. It may be the
case that
+ * the custom serializer uses looser type bounds in its method
+ * declarations.
+ */
+ for (Method method : customSerializer.getMethods()) {
+ if ("serialize".equals(method.getName())) {
+ assert Modifier.isStatic(method.getModifiers()) : "serialize
method "
+ + "in type " + customSerializer.getName() + " must be
static";
+
+ final InvokeCustomFieldSerializerCommand toReturn = new
InvokeCustomFieldSerializerCommand(
+ instanceClass, customSerializer, manuallySerializedType);
+ identityMap.put(instance, toReturn);
+
+ CommandServerSerializationStreamWriter subWriter = new
CommandServerSerializationStreamWriter(
+ clientOracle, new HasValuesCommandSink(toReturn));
+ method.invoke(null, subWriter, instance);
+
+ return toReturn;
+ }
+ }
+
+ throw new NoSuchMethodException(
+ "Could not find serialize method in custom serializer "
+ + customSerializer.getName());
+
+ } catch (SecurityException e) {
+ ex = e;
+ } catch (NoSuchMethodException e) {
+ ex = e;
+ } catch (IllegalArgumentException e) {
+ ex = e;
+ } catch (IllegalAccessException e) {
+ ex = e;
+ } catch (InvocationTargetException e) {
+ ex = e;
+ }
+
+ throw new SerializationException(ex);
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/server/DelegatingClientOracle.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/server/DelegatingClientOracle.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.server;
+
+import com.google.gwt.rpc.client.ast.CommandSink;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+
+/**
+ * A delegate-pattern ClientOracle that can be used to introduce custom
+ * behavior.
+ */
+public class DelegatingClientOracle extends ClientOracle {
+
+ private final ClientOracle delegate;
+
+ public DelegatingClientOracle(ClientOracle delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public CommandSink createCommandSink(OutputStream out) throws
IOException {
+ return delegate.createCommandSink(out);
+ }
+
+ @Override
+ public String createUnusedIdent(String ident) {
+ return delegate.createUnusedIdent(ident);
+ }
+
+ @Override
+ public String getFieldId(Class<?> clazz, String fieldName) {
+ return delegate.getFieldId(clazz, fieldName);
+ }
+
+ @Override
+ public String getFieldId(Enum<?> value) {
+ return delegate.getFieldId(value);
+ }
+
+ @Override
+ public String getFieldId(String className, String fieldName) {
+ return delegate.getFieldId(className, fieldName);
+ }
+
+ @Override
+ public Pair<Class<?>, String> getFieldName(Class<?> clazz, String
fieldId) {
+ return delegate.getFieldName(clazz, fieldId);
+ }
+
+ @Override
+ public String getMethodId(Class<?> clazz, String methodName, Class<?>...
args) {
+ return delegate.getMethodId(clazz, methodName, args);
+ }
+
+ @Override
+ public String getMethodId(String className, String methodName,
+ String... jsniArgTypes) {
+ return delegate.getMethodId(className, methodName, jsniArgTypes);
+ }
+
+ @Override
+ public Field[] getOperableFields(Class<?> clazz) {
+ return delegate.getOperableFields(clazz);
+ }
+
+ @Override
+ public String getSeedName(Class<?> clazz) {
+ return delegate.getSeedName(clazz);
+ }
+
+ @Override
+ public int getTypeId(Class<?> clazz) {
+ return delegate.getTypeId(clazz);
+ }
+
+ @Override
+ public String getTypeName(String seedName) {
+ return delegate.getTypeName(seedName);
+ }
+
+ @Override
+ public boolean isScript() {
+ return delegate.isScript();
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/server/HostedModeClientOracle.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/server/HostedModeClientOracle.java
Mon Jul 6 16:17:17 2009
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.server;
+
+import com.google.gwt.rpc.client.ast.CommandSink;
+import com.google.gwt.rpc.client.impl.SimplePayloadSink;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.server.rpc.impl.SerializabilityUtil;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.lang.reflect.Field;
+
+/**
+ * A ClientOracle that is used for hosted-mode clients. This type only
+ * implements a limited subset of the ClientOracle functionality.
+ */
+public final class HostedModeClientOracle extends ClientOracle {
+
+ @Override
+ public CommandSink createCommandSink(OutputStream out) throws
IOException {
+ final BufferedWriter buffer = new BufferedWriter(new
OutputStreamWriter(
+ out, "UTF-8"));
+
+ return new SimplePayloadSink(buffer) {
+ @Override
+ public void finish() throws SerializationException {
+ super.finish();
+ try {
+ buffer.flush();
+ } catch (IOException e) {
+ throw new SerializationException("Could not flush buffer", e);
+ }
+ }
+ };
+ }
+
+ /**
+ * Unimplemented.
+ */
+ @Override
+ public String createUnusedIdent(String ident) {
+ return unimplemented();
+ }
+
+ /**
+ * Unimplemented.
+ */
+ @Override
+ public String getFieldId(Class<?> clazz, String fieldName) {
+ return unimplemented();
+ }
+
+ /**
+ * Unimplemented.
+ */
+ @Override
+ public String getFieldId(Enum<?> value) {
+ return unimplemented();
+ }
+
+ /**
+ * Unimplemented.
+ */
+ @Override
+ public String getFieldId(String className, String fieldName) {
+ return unimplemented();
+ }
+
+ @Override
+ public Pair<Class<?>, String> getFieldName(Class<?> clazz, String
fieldId) {
+ while (clazz != null) {
+ try {
+ clazz.getDeclaredField(fieldId);
+ return new Pair<Class<?>, String>(clazz, fieldId);
+ } catch (SecurityException e) {
+ // Fall through
+ } catch (NoSuchFieldException e) {
+ // Fall through
+ }
+ clazz = clazz.getSuperclass();
+ }
+ return null;
+ }
+
+ /**
+ * Unimplemented.
+ */
+ @Override
+ public String getMethodId(Class<?> clazz, String methodName, Class<?>...
args) {
+ return unimplemented();
+ }
+
+ /**
+ * Unimplemented.
+ */
+ @Override
+ public String getMethodId(String className, String methodName,
+ String... jsniArgTypes) {
+ return unimplemented();
+ }
+
+ /**
+ * Falls back to reflectively analyzing the provided class.
+ */
+ @Override
+ public Field[] getOperableFields(Class<?> clazz) {
+ return SerializabilityUtil.applyFieldSerializationPolicy(clazz);
+ }
+
+ /**
+ * Unimplemented.
+ */
+ @Override
+ public String getSeedName(Class<?> clazz) {
+ return unimplemented();
+ }
+
+ /**
+ * Unimplemented.
+ */
+ @Override
+ public int getTypeId(Class<?> clazz) {
+ return this.<Integer> unimplemented();
+ }
+
+ /**
+ * Unimplemented.
+ */
+ @Override
+ public String getTypeName(String seedName) {
+ return seedName;
+ }
+
+ /**
+ * Unimplemented.
+ */
+ @Override
+ public boolean isScript() {
+ return false;
+ }
+
+ private <T> T unimplemented() {
+ throw new RuntimeException("Not supported in hosted mode");
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/server/Pair.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/server/Pair.java Mon Jul 6 16:17:17
2009
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.server;
+
+/**
+ * Simple pair class.
+ *
+ * @param <A> any type
+ * @param <B> any type
+ */
+public class Pair<A, B> {
+ private final A a;
+ private final B b;
+
+ Pair(A a, B b) {
+ this.a = a;
+ this.b = b;
+ }
+
+ public A getA() {
+ return a;
+ }
+
+ public B getB() {
+ return b;
+ }
+}
\ No newline at end of file
Added: trunk/user/src/com/google/gwt/rpc/server/RPC.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/server/RPC.java Mon Jul 6 16:17:17
2009
@@ -0,0 +1,469 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.server;
+
+import com.google.gwt.rpc.client.ast.CommandSink;
+import com.google.gwt.rpc.client.ast.HasValues;
+import com.google.gwt.rpc.client.ast.ReturnCommand;
+import com.google.gwt.rpc.client.ast.RpcCommand;
+import com.google.gwt.rpc.client.ast.ThrowCommand;
+import com.google.gwt.rpc.client.impl.HasValuesCommandSink;
+import com.google.gwt.rpc.client.impl.RemoteException;
+import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.server.rpc.RPCRequest;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * EXPERIMENTAL and subject to change. Do not use this in production code.
+ * <p>
+ * Utility class for integrating with the RPC system.
+ */
+public class RPC {
+
+ private static final HashMap<String, Class<?>> TYPE_NAMES = new
HashMap<String, Class<?>>();
+
+ /**
+ * Static map of classes to sets of interfaces (e.g. classes). Optimizes
+ * lookup of interfaces for security.
+ */
+ private static final Map<Class<?>, Set<String>>
serviceToImplementedInterfacesMap = new
com.google.gwt.dev.util.collect.HashMap<Class<?>, Set<String>>();
+
+ static {
+ // The space is needed to prevent name collisions
+ TYPE_NAMES.put(" Z", boolean.class);
+ TYPE_NAMES.put(" B", byte.class);
+ TYPE_NAMES.put(" C", char.class);
+ TYPE_NAMES.put(" D", double.class);
+ TYPE_NAMES.put(" F", float.class);
+ TYPE_NAMES.put(" I", int.class);
+ TYPE_NAMES.put(" J", long.class);
+ TYPE_NAMES.put(" S", short.class);
+ }
+
+ public static RPCRequest decodeRequest(String encodedRequest, Class<?>
type,
+ ClientOracle clientOracle) throws RemoteException {
+ if (encodedRequest == null) {
+ throw new NullPointerException("encodedRequest cannot be null");
+ }
+
+ if (encodedRequest.length() == 0) {
+ throw new IllegalArgumentException("encodedRequest cannot be empty");
+ }
+
+ ClassLoader classLoader =
Thread.currentThread().getContextClassLoader();
+
+ try {
+ SimplePayloadDecoder decoder;
+ try {
+ decoder = new SimplePayloadDecoder(clientOracle, encodedRequest);
+ } catch (ClassNotFoundException e) {
+ throw new IncompatibleRemoteServiceException(
+ "Client does not have a type sent by the server", e);
+ }
+ CommandServerSerializationStreamReader streamReader = new
CommandServerSerializationStreamReader();
+ if (decoder.getThrownValue() != null) {
+
streamReader.prepareToRead(Collections.singletonList(decoder.getThrownValue()));
+ try {
+ throw new RemoteException((Throwable) streamReader.readObject());
+ } catch (ClassCastException e) {
+ throw new SerializationException(
+ "The remote end threw something other than a Throwable", e);
+ } catch (SerializationException e) {
+ throw new IncompatibleRemoteServiceException(
+ "The remote end threw an exception which could not be
deserialized",
+ e);
+ }
+ } else {
+ streamReader.prepareToRead(decoder.getValues());
+ }
+
+ // Read the name of the RemoteService interface
+ String serviceIntfName = streamReader.readString();
+
+ if (type != null) {
+ if (!implementsInterface(type, serviceIntfName)) {
+ // The service does not implement the requested interface
+ throw new IncompatibleRemoteServiceException(
+ "Blocked attempt to access interface '" + serviceIntfName
+ + "', which is not implemented by '" +
printTypeName(type)
+ + "'; this is either misconfiguration or a hack
attempt");
+ }
+ }
+
+ Class<?> serviceIntf;
+ try {
+ serviceIntf = getClassFromSerializedName(null, serviceIntfName,
+ classLoader);
+ if (!RemoteService.class.isAssignableFrom(serviceIntf)) {
+ // The requested interface is not a RpcService interface
+ throw new IncompatibleRemoteServiceException(
+ "Blocked attempt to access interface '"
+ + printTypeName(serviceIntf)
+ + "', which doesn't extend RpcService; "
+ + "this is either misconfiguration or a hack attempt");
+ }
+ } catch (ClassNotFoundException e) {
+ throw new IncompatibleRemoteServiceException(
+ "Could not locate requested interface '" + serviceIntfName
+ + "' in default classloader", e);
+ }
+
+ String serviceMethodName = streamReader.readString();
+
+ int paramCount = streamReader.readInt();
+ Class<?>[] parameterTypes = new Class[paramCount];
+
+ for (int i = 0; i < parameterTypes.length; i++) {
+ String paramClassName = streamReader.readString();
+
+ try {
+ parameterTypes[i] = getClassFromSerializedName(clientOracle,
+ paramClassName, classLoader);
+ } catch (ClassNotFoundException e) {
+ throw new IncompatibleRemoteServiceException("Parameter " + i
+ + " of is of an unknown type '" + paramClassName + "'", e);
+ }
+ }
+
+ try {
+ Method method = serviceIntf.getMethod(serviceMethodName,
parameterTypes);
+
+ Object[] parameterValues = new Object[parameterTypes.length];
+ for (int i = 0; i < parameterValues.length; i++) {
+ Object o =
CommandSerializationUtil.getAccessor(parameterTypes[i]).readNext(
+ streamReader);
+ parameterValues[i] = o;
+ }
+
+ return new RPCRequest(method, parameterValues, null, 0);
+
+ } catch (NoSuchMethodException e) {
+ throw new IncompatibleRemoteServiceException(
+ formatMethodNotFoundErrorMessage(serviceIntf,
serviceMethodName,
+ parameterTypes));
+ }
+ } catch (SerializationException ex) {
+ throw new IncompatibleRemoteServiceException(ex.getMessage(), ex);
+ }
+ }
+
+ public static void invokeAndStreamResponse(Object target,
+ Method serviceMethod, Object[] args, ClientOracle clientOracle,
+ OutputStream stream) throws SerializationException {
+ if (serviceMethod == null) {
+ throw new NullPointerException("serviceMethod");
+ }
+
+ if (clientOracle == null) {
+ throw new NullPointerException("clientOracle");
+ }
+
+ CommandSink sink;
+ try {
+ sink = clientOracle.createCommandSink(stream);
+ } catch (IOException e) {
+ throw new SerializationException("Unable to initialize output", e);
+ }
+
+ try {
+ Object result = serviceMethod.invoke(target, args);
+ try {
+ streamResponse(clientOracle, result, sink, false);
+ } catch (SerializationException e) {
+ streamResponse(clientOracle, e, sink, true);
+ }
+
+ } catch (IllegalAccessException e) {
+ SecurityException securityException = new SecurityException(
+ formatIllegalAccessErrorMessage(target, serviceMethod));
+ securityException.initCause(e);
+ throw securityException;
+ } catch (IllegalArgumentException e) {
+ SecurityException securityException = new SecurityException(
+ formatIllegalArgumentErrorMessage(target, serviceMethod, args));
+ securityException.initCause(e);
+ throw securityException;
+ } catch (InvocationTargetException e) {
+ // Try to encode the caught exception
+ //
+ Throwable cause = e.getCause();
+
+ streamResponse(clientOracle, cause, sink, true);
+ }
+ sink.finish();
+ }
+
+ public static void streamResponseForFailure(ClientOracle clientOracle,
+ OutputStream out, Throwable payload) throws SerializationException {
+ CommandSink sink;
+ try {
+ sink = clientOracle.createCommandSink(out);
+ } catch (IOException e) {
+ throw new SerializationException("Unable to initialize output", e);
+ }
+ streamResponse(clientOracle, payload, sink, true);
+ sink.finish();
+ }
+
+ public static void streamResponseForSuccess(ClientOracle clientOracle,
+ OutputStream out, Object payload) throws SerializationException {
+ CommandSink sink;
+ try {
+ sink = clientOracle.createCommandSink(out);
+ } catch (IOException e) {
+ throw new SerializationException("Unable to initialize output", e);
+ }
+ streamResponse(clientOracle, payload, sink, false);
+ sink.finish();
+ }
+
+ private static String formatIllegalAccessErrorMessage(Object target,
+ Method serviceMethod) {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Blocked attempt to access inaccessible method '");
+ sb.append(getSourceRepresentation(serviceMethod));
+ sb.append("'");
+
+ if (target != null) {
+ sb.append(" on target '");
+ sb.append(printTypeName(target.getClass()));
+ sb.append("'");
+ }
+
+ sb.append("; this is either misconfiguration or a hack attempt");
+
+ return sb.toString();
+ }
+
+ private static String formatIllegalArgumentErrorMessage(Object target,
+ Method serviceMethod, Object[] args) {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Blocked attempt to invoke method '");
+ sb.append(getSourceRepresentation(serviceMethod));
+ sb.append("'");
+
+ if (target != null) {
+ sb.append(" on target '");
+ sb.append(printTypeName(target.getClass()));
+ sb.append("'");
+ }
+
+ sb.append(" with invalid arguments");
+
+ if (args != null && args.length > 0) {
+ sb.append(Arrays.asList(args));
+ }
+
+ return sb.toString();
+ }
+
+ private static String formatMethodNotFoundErrorMessage(Class<?>
serviceIntf,
+ String serviceMethodName, Class<?>[] parameterTypes) {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("Could not locate requested method '");
+ sb.append(serviceMethodName);
+ sb.append("(");
+ for (int i = 0; i < parameterTypes.length; ++i) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(printTypeName(parameterTypes[i]));
+ }
+ sb.append(")'");
+
+ sb.append(" in interface '");
+ sb.append(printTypeName(serviceIntf));
+ sb.append("'");
+
+ return sb.toString();
+ }
+
+ /**
+ * Returns the {@link Class} instance for the named class or primitive
type.
+ *
+ * @param serializedName the serialized name of a class or primitive type
+ * @param classLoader the classLoader used to load {@link Class}es
+ * @return Class instance for the given type name
+ * @throws ClassNotFoundException if the named type was not found
+ */
+ private static Class<?> getClassFromSerializedName(ClientOracle
clientOracle,
+ String serializedName, ClassLoader classLoader)
+ throws ClassNotFoundException {
+ Class<?> value = TYPE_NAMES.get(serializedName);
+ if (value != null) {
+ return value;
+ }
+
+ // Interfaces don't exist in the client, so we use unobfuscated names
+ if (serializedName.charAt(0) == ' ') {
+ serializedName = serializedName.substring(1);
+ } else if (clientOracle != null) {
+ serializedName = clientOracle.getTypeName(serializedName);
+ }
+ assert serializedName != null;
+
+ return Class.forName(serializedName, false, classLoader);
+ }
+
+ /**
+ * Returns the source representation for a method signature.
+ *
+ * @param method method to get the source signature for
+ * @return source representation for a method signature
+ */
+ private static String getSourceRepresentation(Method method) {
+ return method.toString().replace('$', '.');
+ }
+
+ /**
+ * Used to determine whether the specified interface name is implemented
by
+ * the service class. This is done without loading the class (for
security).
+ */
+ private static boolean implementsInterface(Class<?> service, String
intfName) {
+ synchronized (serviceToImplementedInterfacesMap) {
+ // See if it's cached.
+ //
+ Set<String> interfaceSet =
serviceToImplementedInterfacesMap.get(service);
+ if (interfaceSet != null) {
+ if (interfaceSet.contains(intfName)) {
+ return true;
+ }
+ } else {
+ interfaceSet = new HashSet<String>();
+ serviceToImplementedInterfacesMap.put(service, interfaceSet);
+ }
+
+ if (!service.isInterface()) {
+ while ((service != null) && !RpcServlet.class.equals(service)) {
+ Class<?>[] intfs = service.getInterfaces();
+ for (Class<?> intf : intfs) {
+ if (implementsInterfaceRecursive(intf, intfName)) {
+ interfaceSet.add(intfName);
+ return true;
+ }
+ }
+
+ // did not find the interface in this class so we look in the
+ // superclass
+ //
+ service = service.getSuperclass();
+ }
+ } else {
+ if (implementsInterfaceRecursive(service, intfName)) {
+ interfaceSet.add(intfName);
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ /**
+ * Recursive helper for implementsInterface().
+ */
+ private static boolean implementsInterfaceRecursive(Class<?> clazz,
+ String intfName) {
+ assert (clazz.isInterface());
+
+ if (clazz.getName().equals(intfName)) {
+ return true;
+ }
+
+ // search implemented interfaces
+ Class<?>[] intfs = clazz.getInterfaces();
+ for (Class<?> intf : intfs) {
+ if (implementsInterfaceRecursive(intf, intfName)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Straight copy from
+ * {@link
com.google.gwt.dev.util.TypeInfo#getSourceRepresentation(Class)} to
+ * avoid runtime dependency on gwt-dev.
+ */
+ private static String printTypeName(Class<?> type) {
+ // Primitives
+ //
+ if (type.equals(Integer.TYPE)) {
+ return "int";
+ } else if (type.equals(Long.TYPE)) {
+ return "long";
+ } else if (type.equals(Short.TYPE)) {
+ return "short";
+ } else if (type.equals(Byte.TYPE)) {
+ return "byte";
+ } else if (type.equals(Character.TYPE)) {
+ return "char";
+ } else if (type.equals(Boolean.TYPE)) {
+ return "boolean";
+ } else if (type.equals(Float.TYPE)) {
+ return "float";
+ } else if (type.equals(Double.TYPE)) {
+ return "double";
+ }
+
+ // Arrays
+ //
+ if (type.isArray()) {
+ Class<?> componentType = type.getComponentType();
+ return printTypeName(componentType) + "[]";
+ }
+
+ // Everything else
+ //
+ return type.getName().replace('$', '.');
+ }
+
+ private static void streamResponse(ClientOracle clientOracle, Object
payload,
+ CommandSink sink, boolean asThrow) throws SerializationException {
+ HasValues command;
+ if (asThrow) {
+ command = new ThrowCommand();
+ assert payload instanceof Throwable : "Trying to throw something
other than a Throwable";
+ // payload = new RemoteException((Throwable) payload);
+ } else {
+ command = new ReturnCommand();
+ }
+
+ CommandServerSerializationStreamWriter out = new
CommandServerSerializationStreamWriter(
+ clientOracle, new HasValuesCommandSink(command));
+
+ out.writeObject(payload);
+
+ sink.accept((RpcCommand) command);
+ }
+
+ private RPC() {
+ }
+}
Added: trunk/user/src/com/google/gwt/rpc/server/RpcServlet.java
==============================================================================
--- (empty file)
+++ trunk/user/src/com/google/gwt/rpc/server/RpcServlet.java Mon Jul 6
16:17:17 2009
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * 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.google.gwt.rpc.server;
+
+import static
com.google.gwt.user.client.rpc.RpcRequestBuilder.MODULE_BASE_HEADER;
+
+import com.google.gwt.rpc.client.impl.RemoteException;
+import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet;
+import com.google.gwt.user.server.rpc.RPCRequest;
+import com.google.gwt.user.server.rpc.RPCServletUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.ref.SoftReference;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.zip.GZIPOutputStream;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * EXPERIMENTAL and subject to change. Do not use this in production code.
+ * <p>
+ * The servlet base class for your RPC service implementations that
+ * automatically deserializes incoming requests from the client and
serializes
+ * outgoing responses for client/server RPCs.
+ */
+public class RpcServlet extends AbstractRemoteServiceServlet {
+
+ protected static final String CLIENT_ORACLE_EXTENSION = ".gwt.rpc";
+ private static final boolean DUMP_PAYLOAD =
Boolean.getBoolean("gwt.rpc.dumpPayload");
+
+ private final Map<String, SoftReference<ClientOracle>> clientOracleCache
= new HashMap<String, SoftReference<ClientOracle>>();
+
+ /**
+ * The default constructor.
+ */
+ public RpcServlet() {
+ }
+
+ /**
+ * This method creates the ClientOracle that will provide data about the
+ * remote client. It delegates to
+ * {@link #findClientOracleData(String, String)} to obtain access to
+ * ClientOracle data emitted by the GWT compiler.
+ */
+ public ClientOracle getClientOracle() throws SerializationException {
+ String permutationStrongName = getPermutationStrongName();
+ if (permutationStrongName == null) {
+ throw new SecurityException(
+ "Blocked request without GWT permutation header (XSRF attack?)");
+ }
+ String basePath = getRequestModuleBasePath();
+ if (basePath == null) {
+ throw new SecurityException(
+ "Blocked request without GWT base path header (XSRF attack?)");
+ }
+
+ ClientOracle toReturn;
+
+ synchronized (clientOracleCache) {
+ if (clientOracleCache.containsKey(permutationStrongName)) {
+ toReturn = clientOracleCache.get(permutationStrongName).get();
+ if (toReturn != null) {
+ return toReturn;
+ }
+ }
+
+ if ("HostedMode".equals(permutationStrongName)) {
+ if (!allowHostedModeConnections()) {
+ throw new SecurityException("Blocked hosted mode request");
+ }
+ toReturn = new HostedModeClientOracle();
+ } else {
+ InputStream in = findClientOracleData(basePath,
permutationStrongName);
+
+ try {
+ toReturn = WebModeClientOracle.load(in);
+ } catch (IOException e) {
+ throw new SerializationException(
+ "Could not load serialization policy for permutation "
+ + permutationStrongName, e);
+ }
+ }
+ clientOracleCache.put(permutationStrongName,
+ new SoftReference<ClientOracle>(toReturn));
+ }
+
+ return toReturn;
+ }
+
+ /**
+ * Process a call originating from the given request. Uses the
+ * {@link RPC#invokeAndStreamResponse(Object, java.lang.reflect.Method,
Object[], ClientOracle, OutputStream)}
+ * method to do the actual work.
+ * <p>
+ * Subclasses may optionally override this method to handle the payload
in any
+ * way they desire (by routing the request to a framework component, for
+ * instance). The {@link HttpServletRequest} and {@link
HttpServletResponse}
+ * can be accessed via the {@link #getThreadLocalRequest()} and
+ * {@link #getThreadLocalResponse()} methods.
+ * </p>
+ * This is public so that it can be unit tested easily without HTTP.
+ *
+ * @param clientOracle the ClientOracle that will be used to interpret
the
+ * request
+ * @param payload the UTF-8 request payload
+ * @param stream the OutputStream that will receive the encoded response
+ * @throws SerializationException if we cannot serialize the response
+ */
+ public void processCall(ClientOracle clientOracle, String payload,
+ OutputStream stream) throws SerializationException {
+ assert clientOracle != null : "clientOracle";
+ assert payload !
==============================================================================
Diff truncated at 200k characters