]> source.dussan.org Git - vaadin-framework.git/commitdiff
Handle ClientMethodInvocation serialization with JSONArray as parameter
authorPekka Hyvönen <pekka@vaadin.com>
Fri, 11 Oct 2013 09:38:53 +0000 (12:38 +0300)
committerVaadin Code Review <review@vaadin.com>
Fri, 11 Oct 2013 11:50:04 +0000 (11:50 +0000)
(#12532)

Change-Id: I67306d2b9d151614f72455063b0d01423aeed4c1

server/src/com/vaadin/server/ClientMethodInvocation.java
server/tests/src/com/vaadin/tests/server/TestClientMethodSerialization.java [new file with mode: 0644]

index 9c8318b0641d104a73cb449806e9787de31c163e..3a6a87a53ca954e0119f850e23cd672b8330fb78 100644 (file)
 
 package com.vaadin.server;
 
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.Serializable;
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
 
+import org.json.JSONArray;
+import org.json.JSONException;
+
 /**
  * Internal class for keeping track of pending server to client method
  * invocations for a Connector.
@@ -80,4 +86,52 @@ public class ClientMethodInvocation implements Serializable,
         }
         return Long.signum(getSequenceNumber() - o.getSequenceNumber());
     }
+
+    private void writeObject(ObjectOutputStream stream) throws IOException {
+        // Need to have custom serialization and deserialization because the
+        // constructor allows parameters of any type with Object[]. Thus, having
+        // parameters that are not Serializable will lead to
+        // NotSerializableException when trying to serialize this class.
+        // An example case of this is in #12532 (JavaScriptCallbackHelper ->
+        // JSONArray as parameter and not Serializable), for which this
+        // hac..workaround is implemented.
+
+        // Goes through the parameter types, and apply "custom serialization" to
+        // the ones that are not Serializable by changing them into something
+        // that is Serializable. On deserialization (readObject-method below)
+        // the process should be reversed.
+
+        // Easy way for implementing serialization & deserialization is by
+        // writing/parsing the object's content as string.
+        for (int i = 0; i < parameterTypes.length; i++) {
+            Type type = parameterTypes[i];
+            if (type instanceof Class<?>) {
+                Class<?> clazz = (Class<?>) type;
+                if (JSONArray.class.isAssignableFrom(clazz)) {
+                    parameters[i] = ((JSONArray) parameters[i]).toString();
+                }
+            }
+        }
+        stream.defaultWriteObject();
+    }
+
+    private void readObject(ObjectInputStream stream) throws IOException,
+            ClassNotFoundException {
+        // Reverses the serialization done in writeObject. Basically just
+        // parsing the serialized type back to the non-serializable type.
+        stream.defaultReadObject();
+        for (int i = 0; i < parameterTypes.length; i++) {
+            Type type = parameterTypes[i];
+            if (type instanceof Class<?>) {
+                Class<?> clazz = (Class<?>) type;
+                if (JSONArray.class.isAssignableFrom(clazz)) {
+                    try {
+                        parameters[i] = new JSONArray(((String) parameters[i]));
+                    } catch (JSONException e) {
+                        throw new IOException(e);
+                    }
+                }
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/server/tests/src/com/vaadin/tests/server/TestClientMethodSerialization.java b/server/tests/src/com/vaadin/tests/server/TestClientMethodSerialization.java
new file mode 100644 (file)
index 0000000..1e0210d
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.server;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+import org.json.JSONArray;
+
+import com.vaadin.server.ClientMethodInvocation;
+import com.vaadin.server.JavaScriptCallbackHelper;
+import com.vaadin.ui.JavaScript.JavaScriptCallbackRpc;
+import com.vaadin.util.ReflectTools;
+
+public class TestClientMethodSerialization extends TestCase {
+
+    private static final Method JAVASCRIPT_CALLBACK_METHOD = ReflectTools
+            .findMethod(JavaScriptCallbackRpc.class, "call", String.class,
+                    JSONArray.class);
+
+    private static final Method BASIC_PARAMS_CALL_METHOD = ReflectTools
+            .findMethod(TestClientMethodSerialization.class,
+                    "basicParamsMethodForTesting", String.class, Integer.class);
+
+    private static final Method NO_PARAMS_CALL_METHOD = ReflectTools
+            .findMethod(TestClientMethodSerialization.class,
+                    "noParamsMethodForTesting");
+
+    public void basicParamsMethodForTesting(String stringParam,
+            Integer integerParam) {
+    }
+
+    public void noParamsMethodForTesting() {
+    }
+
+    /**
+     * Tests the {@link ClientMethodInvocation} serialization when using
+     * {@link JavaScriptCallbackHelper#invokeCallback(String, Object...)}.
+     * #12532
+     */
+    public void testClientMethodSerialization_WithJSONArray_ContentStaysSame()
+            throws Exception {
+        JSONArray originalArray = new JSONArray(Arrays.asList(
+                "callbackParameter1", "callBackParameter2", "12345"));
+        ClientMethodInvocation original = new ClientMethodInvocation(null,
+                "interfaceName", JAVASCRIPT_CALLBACK_METHOD, new Object[] {
+                        "callBackMethodName", originalArray });
+
+        ClientMethodInvocation copy = (ClientMethodInvocation) serializeAndDeserialize(original);
+        JSONArray copyArray = (JSONArray) copy.getParameters()[1];
+        assertEquals(originalArray.toString(), copyArray.toString());
+    }
+
+    public void testClientMethodSerialization_WithBasicParams_NoChanges()
+            throws Exception {
+        String stringParam = "a string 123";
+        Integer integerParam = 1234567890;
+        ClientMethodInvocation original = new ClientMethodInvocation(null,
+                "interfaceName", BASIC_PARAMS_CALL_METHOD, new Serializable[] {
+                        stringParam, integerParam });
+        ClientMethodInvocation copy = (ClientMethodInvocation) serializeAndDeserialize(original);
+        String copyString = (String) copy.getParameters()[0];
+        Integer copyInteger = (Integer) copy.getParameters()[1];
+        assertEquals(copyString, stringParam);
+        assertEquals(copyInteger, integerParam);
+    }
+
+    public void testClientMethodSerialization_NoParams_NoExceptions() {
+        ClientMethodInvocation original = new ClientMethodInvocation(null,
+                "interfaceName", NO_PARAMS_CALL_METHOD, null);
+        ClientMethodInvocation copy = (ClientMethodInvocation) serializeAndDeserialize(original);
+    }
+
+    private static Serializable serializeAndDeserialize(Serializable input) {
+        Serializable output = null;
+        try {
+            ByteArrayOutputStream bs = new ByteArrayOutputStream();
+            ObjectOutputStream out = new ObjectOutputStream(bs);
+            out.writeObject(input);
+            byte[] data = bs.toByteArray();
+            ObjectInputStream in = new ObjectInputStream(
+                    new ByteArrayInputStream(data));
+            output = (Serializable) in.readObject();
+        } catch (Exception e) {
+            fail("Exception during serialization/deserialization: "
+                    + e.getMessage());
+        }
+        return output;
+    }
+
+}