From fe2e05c459b4a329194ab8ec7dff3ecdb627d155 Mon Sep 17 00:00:00 2001
From: pkupczyk <pkupczyk>
Date: Tue, 5 Jun 2012 09:41:39 +0000
Subject: [PATCH] SP-82 BIS-14 Query Plugins: improve the JSON-RPC
 implementation to also accept custom classes and not only primitive types and
 collections

SVN: 25560
---
 .../JsonContainerDeserializer.java            | 26 ++++++++--
 .../server/json/JsonDeserializationTest.java  | 49 ++++++++++++++++++-
 .../api/server/json/JsonTestObjectMapper.java |  2 +
 .../json/object/ObjectWithPrivateAccess.java  | 46 +++++++++++++++++
 4 files changed, 116 insertions(+), 7 deletions(-)
 create mode 100644 openbis-common/sourceTest/java/ch/systemsx/cisd/common/api/server/json/object/ObjectWithPrivateAccess.java

diff --git a/openbis-common/source/java/ch/systemsx/cisd/common/api/server/json/deserializer/JsonContainerDeserializer.java b/openbis-common/source/java/ch/systemsx/cisd/common/api/server/json/deserializer/JsonContainerDeserializer.java
index a70747e1ced..d8eeae2e3ad 100644
--- a/openbis-common/source/java/ch/systemsx/cisd/common/api/server/json/deserializer/JsonContainerDeserializer.java
+++ b/openbis-common/source/java/ch/systemsx/cisd/common/api/server/json/deserializer/JsonContainerDeserializer.java
@@ -17,6 +17,8 @@
 package ch.systemsx.cisd.common.api.server.json.deserializer;
 
 import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.EnumMap;
@@ -36,6 +38,8 @@ import org.codehaus.jackson.map.JsonMappingException;
 import org.codehaus.jackson.map.deser.BeanDeserializer;
 import org.codehaus.jackson.map.deser.BeanDeserializerFactory;
 import org.codehaus.jackson.map.deser.SettableBeanProperty;
+import org.codehaus.jackson.map.introspect.BasicBeanDescription;
+import org.codehaus.jackson.map.util.ClassUtil;
 import org.codehaus.jackson.type.JavaType;
 
 import ch.systemsx.cisd.common.api.server.json.common.JsonConstants;
@@ -229,7 +233,7 @@ public class JsonContainerDeserializer extends JsonDeserializer<Object>
 
             try
             {
-                Object instance = tryCreateInstance(clazz);
+                Object instance = tryCreateInstance(clazz, ctxt);
 
                 Iterator<SettableBeanProperty> properties = tryGetProperties(clazz, ctxt);
                 if (properties != null)
@@ -276,17 +280,29 @@ public class JsonContainerDeserializer extends JsonDeserializer<Object>
         return clazz;
     }
 
-    private Object tryCreateInstance(Class<?> clazz) throws IOException
+    private Object tryCreateInstance(Class<?> clazz, DeserializationContext ctxt)
+            throws IOException
     {
         try
         {
-            return clazz.newInstance();
+            JavaType type = ctxt.getConfig().getTypeFactory().constructType(clazz);
+            BasicBeanDescription beanDesc = ctxt.getConfig().introspect(type);
+            Constructor<?> defaultConstructor = beanDesc.findDefaultConstructor();
+            ClassUtil.checkAndFixAccess(defaultConstructor);
+            return defaultConstructor.newInstance();
+
         } catch (InstantiationException e)
         {
-            return new JsonMappingException("Couldn't create an instance of class: " + clazz, e);
+            throw new JsonMappingException("Couldn't create an instance of class: " + clazz, e);
         } catch (IllegalAccessException e)
         {
-            return new JsonMappingException("Couldn't create an instance of class: " + clazz, e);
+            throw new JsonMappingException("Couldn't create an instance of class: " + clazz, e);
+        } catch (IllegalArgumentException e)
+        {
+            throw new JsonMappingException("Couldn't create an instance of class: " + clazz, e);
+        } catch (InvocationTargetException e)
+        {
+            throw new JsonMappingException("Couldn't create an instance of class: " + clazz, e);
         }
     }
 
diff --git a/openbis-common/sourceTest/java/ch/systemsx/cisd/common/api/server/json/JsonDeserializationTest.java b/openbis-common/sourceTest/java/ch/systemsx/cisd/common/api/server/json/JsonDeserializationTest.java
index 72684d2f845..6e8aaa34c50 100644
--- a/openbis-common/sourceTest/java/ch/systemsx/cisd/common/api/server/json/JsonDeserializationTest.java
+++ b/openbis-common/sourceTest/java/ch/systemsx/cisd/common/api/server/json/JsonDeserializationTest.java
@@ -39,6 +39,7 @@ import ch.systemsx.cisd.common.api.server.json.object.ObjectWithNestedTypes;
 import ch.systemsx.cisd.common.api.server.json.object.ObjectWithNestedTypes.ObjectNested;
 import ch.systemsx.cisd.common.api.server.json.object.ObjectWithNestedTypes.ObjectNestedChild;
 import ch.systemsx.cisd.common.api.server.json.object.ObjectWithPrimitiveTypes;
+import ch.systemsx.cisd.common.api.server.json.object.ObjectWithPrivateAccess;
 import ch.systemsx.cisd.common.api.server.json.object.ObjectWithRenamedProperties;
 import ch.systemsx.cisd.common.api.server.json.object.ObjectWithType;
 import ch.systemsx.cisd.common.api.server.json.object.ObjectWithTypeA;
@@ -209,6 +210,13 @@ public class JsonDeserializationTest
         testDeserializeObjectWithRenamedProperties("@class", ".LegacyObjectWithRenamedProperties");
     }
 
+    @Test
+    public void testDeserializeJsonWithPrivateAccess() throws Exception
+    {
+        testDeserializeObjectWithPrivateAccess("@type", "ObjectWithPrivateAccess");
+        testDeserializeObjectWithPrivateAccess("@class", ".LegacyObjectWithPrivateAccess");
+    }
+
     @Test(expectedExceptions = JsonMappingException.class)
     public void testDeserializeJsonWithoutTypeToPolymorphicType() throws Exception
     {
@@ -480,6 +488,23 @@ public class JsonDeserializationTest
         Assert.assertEquals(object.getY(), "propertyWithGetterAndSetterRenamedValue");
     }
 
+    private void testDeserializeObjectWithPrivateAccess(String typeField, String typeValue)
+            throws Exception
+    {
+        Map<String, Object> objectMap = new HashMap<String, Object>();
+        if (typeField != null && typeValue != null)
+        {
+            objectMap.put(typeField, typeValue);
+        }
+
+        objectMap.put("field", "fieldValue");
+
+        ObjectWithPrivateAccess object = deserialize(objectMap, ObjectWithPrivateAccess.class);
+
+        Assert.assertNotNull(object);
+        Assert.assertEquals(object.getField(), "fieldValue");
+    }
+
     private void testDeserializeObjectWithPrimitiveTypes(String typeField, String typeValue)
             throws Exception
     {
@@ -682,6 +707,14 @@ public class JsonDeserializationTest
         return itemWithUnknownClass;
     }
 
+    private Map<String, Object> createItemWithPrivateAccess()
+    {
+        Map<String, Object> itemWithPrivateAccess = new HashMap<String, Object>();
+        itemWithPrivateAccess.put("@class", ".LegacyObjectWithPrivateAccess");
+        itemWithPrivateAccess.put("field", "itemWithPrivateAccess_field");
+        return itemWithPrivateAccess;
+    }
+
     @SuppressWarnings(
         { "rawtypes", "unchecked" })
     private void assertItemWithUnknownClass(Object object)
@@ -690,6 +723,13 @@ public class JsonDeserializationTest
         Assert.assertEquals(new HashMap((Map) object), createItemWithUnknownClass());
     }
 
+    private void assertItemWithPrivateAccess(Object object)
+    {
+        Assert.assertEquals(object.getClass(), ObjectWithPrivateAccess.class);
+        ObjectWithPrivateAccess itemWithPrivateAccess = (ObjectWithPrivateAccess) object;
+        Assert.assertEquals("itemWithPrivateAccess_field", itemWithPrivateAccess.getField());
+    }
+
     @SuppressWarnings(
         { "rawtypes", "unchecked" })
     private Map<String, Object> createObjectWithContainerTypes()
@@ -700,6 +740,7 @@ public class JsonDeserializationTest
         collection.add(createItemWithKnownButNotUniqueClass());
         collection.add(createItemWithUnknownType());
         collection.add(createItemWithUnknownClass());
+        collection.add(createItemWithPrivateAccess());
 
         Map map = new HashMap();
         map.put("itemWithKnownType", createItemWithKnownType());
@@ -707,6 +748,7 @@ public class JsonDeserializationTest
         map.put("itemWithKnownButNotUniqueClass", createItemWithKnownButNotUniqueClass());
         map.put("itemWithUnknownType", createItemWithUnknownType());
         map.put("itemWithUnknownClass", createItemWithUnknownClass());
+        map.put("itemWithPrivateAccess", createItemWithPrivateAccess());
 
         List list = new ArrayList();
         list.add(createItemWithKnownType());
@@ -714,6 +756,7 @@ public class JsonDeserializationTest
         list.add(createItemWithKnownButNotUniqueClass());
         list.add(createItemWithUnknownType());
         list.add(createItemWithUnknownClass());
+        list.add(createItemWithPrivateAccess());
 
         Map<String, Object> object = new HashMap<String, Object>();
 
@@ -752,25 +795,27 @@ public class JsonDeserializationTest
         { "rawtypes" })
     private void assertItemsCollection(Collection collection)
     {
-        Assert.assertEquals(collection.size(), 5);
+        Assert.assertEquals(collection.size(), 6);
         Iterator iterator = collection.iterator();
         assertItemWithKnownType(iterator.next());
         assertItemWithKnownUniqueClass(iterator.next());
         assertItemWithKnownButNotUniqueClass(iterator.next());
         assertItemWithUnknownType(iterator.next());
         assertItemWithUnknownClass(iterator.next());
+        assertItemWithPrivateAccess(iterator.next());
     }
 
     @SuppressWarnings(
         { "rawtypes" })
     private void assertItemsMap(Map map)
     {
-        Assert.assertEquals(map.size(), 5);
+        Assert.assertEquals(map.size(), 6);
         assertItemWithKnownType(map.get("itemWithKnownType"));
         assertItemWithKnownUniqueClass(map.get("itemWithKnownUniqueClass"));
         assertItemWithKnownButNotUniqueClass(map.get("itemWithKnownButNotUniqueClass"));
         assertItemWithUnknownType(map.get("itemWithUnknownType"));
         assertItemWithUnknownClass(map.get("itemWithUnknownClass"));
+        assertItemWithPrivateAccess(map.get("itemWithPrivateAccess"));
     }
 
     @SuppressWarnings("unchecked")
diff --git a/openbis-common/sourceTest/java/ch/systemsx/cisd/common/api/server/json/JsonTestObjectMapper.java b/openbis-common/sourceTest/java/ch/systemsx/cisd/common/api/server/json/JsonTestObjectMapper.java
index ea1502a0c33..d562c2a3f51 100644
--- a/openbis-common/sourceTest/java/ch/systemsx/cisd/common/api/server/json/JsonTestObjectMapper.java
+++ b/openbis-common/sourceTest/java/ch/systemsx/cisd/common/api/server/json/JsonTestObjectMapper.java
@@ -30,6 +30,7 @@ import ch.systemsx.cisd.common.api.server.json.object.ObjectWithNestedTypes;
 import ch.systemsx.cisd.common.api.server.json.object.ObjectWithNestedTypes.ObjectNested;
 import ch.systemsx.cisd.common.api.server.json.object.ObjectWithNestedTypes.ObjectNestedChild;
 import ch.systemsx.cisd.common.api.server.json.object.ObjectWithPrimitiveTypes;
+import ch.systemsx.cisd.common.api.server.json.object.ObjectWithPrivateAccess;
 import ch.systemsx.cisd.common.api.server.json.object.ObjectWithRenamedProperties;
 import ch.systemsx.cisd.common.api.server.json.object.ObjectWithType;
 import ch.systemsx.cisd.common.api.server.json.object.ObjectWithTypeA;
@@ -75,6 +76,7 @@ public class JsonTestObjectMapper extends ObjectMapper
                 ObjectWithIgnoredProperties.class);
         classMapping.addClass(".LegacyObjectWithRenamedProperties",
                 ObjectWithRenamedProperties.class);
+        classMapping.addClass(".LegacyObjectWithPrivateAccess", ObjectWithPrivateAccess.class);
 
         setAnnotationIntrospector(new JsonTypeAndClassAnnotationIntrospector(classMapping));
         setSubtypeResolver(new JsonReflectionsSubTypeResolver(subTypesMapping));
diff --git a/openbis-common/sourceTest/java/ch/systemsx/cisd/common/api/server/json/object/ObjectWithPrivateAccess.java b/openbis-common/sourceTest/java/ch/systemsx/cisd/common/api/server/json/object/ObjectWithPrivateAccess.java
new file mode 100644
index 00000000000..6eaad338c61
--- /dev/null
+++ b/openbis-common/sourceTest/java/ch/systemsx/cisd/common/api/server/json/object/ObjectWithPrivateAccess.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.common.api.server.json.object;
+
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author pkupczyk
+ */
+
+@JsonObject("ObjectWithPrivateAccess")
+public class ObjectWithPrivateAccess
+{
+
+    private String field;
+
+    private ObjectWithPrivateAccess()
+    {
+    }
+
+    public String getField()
+    {
+        return field;
+    }
+
+    @SuppressWarnings("unused")
+    private void setField(String field)
+    {
+        this.field = field;
+    }
+
+}
-- 
GitLab