You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by ie...@apache.org on 2008/07/19 15:51:04 UTC

svn commit: r678158 - in /incubator/shindig/trunk: ./ java/social-api/ java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/ java/social-api/src/test/java/org/apache/shindig/social/ java/social-api/src/test/java/org/apache/shindig/so...

Author: ieb
Date: Sat Jul 19 06:51:03 2008
New Revision: 678158

URL: http://svn.apache.org/viewvc?rev=678158&view=rev
Log:
SHINDIG-451
Applying patch from the JIRA.

Adds a json-lib (net.sf) converter, not wired into social-api at present.

Added:
    incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/BeanJsonLibConversionException.java
    incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/BeanJsonLibConverter.java
    incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/JSONLibConverterUtils.java
    incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/JSONObjectToMapMorpher.java
    incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/JsonLibTestsGuiceModule.java
    incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/APIValidator.java
    incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/APIValidatorExpcetion.java
    incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/APIValidatorTest.java
    incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/BeanJsonLibConverterTest.java
    incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/JsonConverterPerformancePerf.java
Modified:
    incubator/shindig/trunk/java/social-api/pom.xml
    incubator/shindig/trunk/pom.xml

Modified: incubator/shindig/trunk/java/social-api/pom.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/pom.xml?rev=678158&r1=678157&r2=678158&view=diff
==============================================================================
--- incubator/shindig/trunk/java/social-api/pom.xml (original)
+++ incubator/shindig/trunk/java/social-api/pom.xml Sat Jul 19 06:51:03 2008
@@ -60,12 +60,31 @@
       <groupId>org.apache.shindig</groupId>
       <artifactId>shindig-common</artifactId>
     </dependency>
+    <!-- is this Ok, only used in test to load the features -->
+    <dependency>
+      <groupId>org.apache.shindig</groupId>
+      <artifactId>shindig-features</artifactId>
+      <scope>test</scope>
+    </dependency>
 
     <!-- external depenencies -->
     <dependency>
       <groupId>org.json</groupId>
       <artifactId>json</artifactId>
     </dependency>
+    <!-- the classifier prevents this working in dep management -->
+    <dependency>
+      <groupId>net.sf.json-lib</groupId>
+      <artifactId>json-lib</artifactId>
+      <version>2.2</version>
+      <classifier>jdk15</classifier>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>rhino</groupId>
+      <artifactId>js</artifactId>
+      <scope>test</scope>
+    </dependency>
     <dependency>
       <groupId>com.google.code.guice</groupId>
       <artifactId>guice</artifactId>
@@ -112,5 +131,10 @@
       <artifactId>core</artifactId>
       <scope>compile</scope>
     </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 </project>

Added: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/BeanJsonLibConversionException.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/BeanJsonLibConversionException.java?rev=678158&view=auto
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/BeanJsonLibConversionException.java (added)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/BeanJsonLibConversionException.java Sat Jul 19 06:51:03 2008
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.social.opensocial.util;
+
+/**
+ * Where a conversion exception happens in the Json Lib conversion, this
+ * exception is thrown.
+ */
+public class BeanJsonLibConversionException extends RuntimeException {
+
+  private static final long serialVersionUID = -8609384443448202372L;
+
+  public BeanJsonLibConversionException() {
+  }
+
+  public BeanJsonLibConversionException(String arg0) {
+    super(arg0);
+  }
+
+  public BeanJsonLibConversionException(Throwable arg0) {
+    super(arg0);
+  }
+
+  public BeanJsonLibConversionException(String arg0, Throwable arg1) {
+    super(arg0, arg1);
+  }
+
+}

Added: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/BeanJsonLibConverter.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/BeanJsonLibConverter.java?rev=678158&view=auto
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/BeanJsonLibConverter.java (added)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/BeanJsonLibConverter.java Sat Jul 19 06:51:03 2008
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.social.opensocial.util;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.sf.ezmorph.MorpherRegistry;
+import net.sf.json.JSONArray;
+import net.sf.json.JSONException;
+import net.sf.json.JSONObject;
+import net.sf.json.JsonConfig;
+import net.sf.json.processors.DefaultValueProcessor;
+import net.sf.json.util.EnumMorpher;
+import net.sf.json.util.JSONUtils;
+import net.sf.json.util.NewBeanInstanceStrategy;
+import net.sf.json.util.PropertyFilter;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shindig.social.opensocial.model.Address;
+import org.apache.shindig.social.opensocial.model.Email;
+import org.apache.shindig.social.opensocial.model.Enum;
+import org.apache.shindig.social.opensocial.model.MediaItem;
+import org.apache.shindig.social.opensocial.model.Organization;
+import org.apache.shindig.social.opensocial.model.Phone;
+import org.apache.shindig.social.opensocial.model.Url;
+
+
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+
+/**
+ * 
+ */
+public class BeanJsonLibConverter implements BeanConverter {
+
+  protected static final Log log = LogFactory
+      .getLog(BeanJsonLibConverter.class);
+  private Injector injector;
+  private JsonConfig jsonConfig;
+  private boolean debugMode = false;
+
+  /*
+   * Register the Enum Morphers so that JSON -> Bean works correctly for enums.
+   */
+  static {
+    MorpherRegistry morpherRegistry = JSONUtils.getMorpherRegistry();
+    morpherRegistry.registerMorpher(new EnumMorpher(Address.Field.class));
+    morpherRegistry.registerMorpher(new EnumMorpher(Phone.Field.class));
+    morpherRegistry.registerMorpher(new EnumMorpher(Email.Field.class));
+    morpherRegistry.registerMorpher(new EnumMorpher(MediaItem.Field.class));
+    morpherRegistry.registerMorpher(new EnumMorpher(MediaItem.Type.class));
+    morpherRegistry.registerMorpher(new EnumMorpher(Enum.Drinker.class));
+    morpherRegistry.registerMorpher(new EnumMorpher(Enum.Field.class));
+    morpherRegistry.registerMorpher(new EnumMorpher(Enum.Gender.class));
+    morpherRegistry
+        .registerMorpher(new EnumMorpher(Enum.NetworkPresence.class));
+    morpherRegistry.registerMorpher(new EnumMorpher(Enum.Smoker.class));
+
+    morpherRegistry.registerMorpher(new JSONObjectToMapMorpher());
+  }
+
+  @Inject
+  public BeanJsonLibConverter(Injector injector) {
+    this.injector = injector;
+    createJsonConfig();
+  }
+
+  @SuppressWarnings("unchecked")
+  public <T> T convertToObject(String string, final Class<T> rootBeanClass) {
+
+    if (string.startsWith("[")) {
+      JSONArray jsonArray = JSONArray.fromObject(string, jsonConfig);
+      if (debugMode) {
+        JSONLibConverterUtils.dumpJsonArray(jsonArray, " ");
+      }
+
+      if (rootBeanClass.isArray()) {
+        Class<?> componentType = rootBeanClass.getComponentType();
+        Object rootObject = injector.getInstance(componentType);
+        List<?> o = JSONArray.toList(jsonArray, rootObject, jsonConfig);
+        Object[] result = (Object[]) Array.newInstance(componentType, o.size());
+        for (int i = 0; i < o.size(); i++) {
+          result[i] = o.get(i);
+        }
+        return (T) result;
+
+      } else {
+        T rootObject = injector.getInstance(rootBeanClass);
+        Object o = JSONArray.toArray(jsonArray, rootObject, jsonConfig);
+        return (T) o;
+      }
+    } else {
+      JSONObject jsonObject = JSONObject.fromObject(string, jsonConfig);
+
+      if (debugMode) {
+        JSONLibConverterUtils.dumpJsonObject(jsonObject, " ");
+      }
+
+      T rootObject = injector.getInstance(rootBeanClass);
+      Object o = JSONObject.toBean(jsonObject, rootObject, jsonConfig);
+      return (T) o;
+
+    }
+  }
+
+  /**
+   * @return
+   */
+  private void createJsonConfig() {
+
+    jsonConfig = new JsonConfig();
+
+    /*
+     * This hook deals with the creation of new beans in the JSON -> Java Bean
+     * conversion
+     */
+    jsonConfig.setNewBeanInstanceStrategy(new NewBeanInstanceStrategy() {
+
+      @SuppressWarnings("unchecked")
+      @Override
+      public Object newInstance(Class beanClass, JSONObject jsonObject)
+          throws InstantiationException, IllegalAccessException,
+          SecurityException, NoSuchMethodException, InvocationTargetException {
+        if (beanClass != null) {
+          Object o = BeanJsonLibConverter.this.injector.getInstance(beanClass);
+          if (debugMode) {
+            log.info("Created Object " + o + " for " + beanClass + " with ["
+                + jsonObject + "]");
+          }
+          return o;
+        }
+        return DEFAULT.newInstance(beanClass, jsonObject);
+      }
+
+    });
+
+    /*
+     * We are expecting null for nulls
+     */
+    jsonConfig.registerDefaultValueProcessor(String.class,
+        new DefaultValueProcessor() {
+          @SuppressWarnings("unchecked")
+          public Object getDefaultValue(Class target) {
+            return null;
+          }
+        });
+    
+    jsonConfig.setJsonPropertyFilter(new PropertyFilter() {
+
+      public boolean apply(Object source, String name, Object value) {
+        return filterProperty(source, name, value);
+      }
+      
+    });
+    
+    jsonConfig.setJavaPropertyFilter(new PropertyFilter() {
+
+      public boolean apply(Object source, String name, Object value) {
+        return filterProperty(source,name,value);
+      }
+      
+    });
+
+    // the classMap deals with the basic json string to bean conversion
+
+    Map<String, Class<?>> classMap = new HashMap<String, Class<?>>();
+
+    /*
+     * mappings are required where there is a List of objects in the interface
+     * with no indication of what type the list should contain. At the moment,
+     * we are using 1 map for all json trees, as there is no conflict, but if
+     * there is a map could be selected on the basis of the root object. It
+     * would be better to do this with generics, but this is good enough and
+     * compact enough for the moment.
+     * 
+     */
+    //
+    // activity
+    classMap.put("mediaItems", MediaItem.class);
+    // this may not be necessary
+    classMap.put("templateParams", Map.class);
+    // BodyType needs no mappings
+    // Message needs no mappings
+    // Name needs no mappings
+    // Organization needs no mappings
+    // Url needs no mappings
+    // Email needs no mappings
+    // Phone Needs no mappings
+    // Address Needs no mappings
+    // MediaItem needs no mappings
+
+    // Person map
+    classMap.put("addresses", Address.class);
+    classMap.put("phoneNumbers", Phone.class);
+    classMap.put("emails", Email.class);
+    classMap.put("mediaItems", MediaItem.class);
+    classMap.put("jobs", Organization.class);
+    classMap.put("schools", Organization.class);
+    classMap.put("urls", Url.class);
+    jsonConfig.setClassMap(classMap);
+
+  }
+
+  /**
+   * @param source
+   * @param name
+   * @param value
+   * @return
+   */
+  protected boolean filterProperty(Object source, String name, Object value) {
+    if ( value == null ) {
+      return true;
+    }
+    if ( value instanceof JSONArray ) {
+      JSONArray array = (JSONArray) value;
+      if ( array.size() == 0 ) {
+        return true;
+      }
+    }
+    if ( value instanceof JSONObject ) {
+      JSONObject object = (JSONObject) value;
+      if ( object.isNullObject() || object.isEmpty()  ) {
+        return true;
+      }
+    }
+    if ( value instanceof Collection) {
+      Collection<?> collection = (Collection<?>) value;
+      if ( collection.size() == 0  ) {
+        return true;
+      }
+    }
+    if ( value instanceof Object[] ) {
+      Object[] oarray = (Object[]) value;
+      if ( oarray.length == 0 ) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public String convertToString(Object pojo) {
+
+    try {
+      JSONObject jsonObject = JSONObject.fromObject(pojo, jsonConfig);
+      return jsonObject.toString();
+    } catch (JSONException jse) {
+      Class<?> pojoClass = pojo.getClass();
+      if (JSONUtils.isArray(pojoClass)) {
+        JSONArray jsonArray = JSONArray.fromObject(pojo);
+        String result = jsonArray.toString();
+        return result;
+      }
+      throw jse;
+    }
+  }
+
+  /**
+   * @param string
+   * @param class1
+   */
+  public void addMapping(String key, Class<?> class1) {
+    jsonConfig.getClassMap().put(key, class1);
+  }
+
+}

Added: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/JSONLibConverterUtils.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/JSONLibConverterUtils.java?rev=678158&view=auto
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/JSONLibConverterUtils.java (added)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/JSONLibConverterUtils.java Sat Jul 19 06:51:03 2008
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.social.opensocial.util;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+
+/**
+ *
+ */
+public class JSONLibConverterUtils {
+  protected static final Log log = LogFactory
+  .getLog(JSONLibConverterUtils.class);
+
+  
+  /**
+   * @param jsonObject
+   */
+  public static final void dumpJsonObject(JSONObject jsonObject, String indent) {
+    for (Object key : jsonObject.keySet()) {
+      Object value = jsonObject.get(key);
+      if (value instanceof JSONObject) {
+        log.info(indent + key + ":JSONObject");
+        dumpJsonObject((JSONObject) value, indent + "  ");
+      } else if (value instanceof JSONArray) {
+        log.info(indent + key + ":JSONArray " + ((JSONArray) value).size());
+        dumpJsonArray((JSONArray) value, indent + "  ");
+      } else {
+        log.info(indent + key + ":" + value + ":"
+            + (value == null ? "na" : value.getClass()));
+      }
+    }
+  }
+
+  
+  /**
+   * @param value
+   * @param string
+   */
+  static void dumpJsonArray(JSONArray array, String indent) {
+    for (Object value : array) {
+      if (value instanceof JSONObject) {
+        log.info(indent + ":JSONObject");
+        dumpJsonObject((JSONObject) value, indent + "  ");
+      } else if (value instanceof JSONArray) {
+        log.info(indent + ":JSONArray " + ((JSONArray) value).size());
+        dumpJsonArray((JSONArray) value, indent + "  ");
+      } else {
+        log.info(indent + ":" + value + ":"
+            + (value == null ? "na" : value.getClass()));
+      }
+    }
+  }
+}

Added: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/JSONObjectToMapMorpher.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/JSONObjectToMapMorpher.java?rev=678158&view=auto
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/JSONObjectToMapMorpher.java (added)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/util/JSONObjectToMapMorpher.java Sat Jul 19 06:51:03 2008
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.social.opensocial.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import net.sf.ezmorph.Morpher;
+import net.sf.ezmorph.ObjectMorpher;
+import net.sf.json.JSONObject;
+
+/**
+ *
+ */
+public class JSONObjectToMapMorpher implements Morpher, ObjectMorpher {
+
+  public Class<?> morphsTo() {
+    return Map.class;
+  }
+
+  @SuppressWarnings("unchecked")
+  public boolean supports(Class clazz) {    
+    return (JSONObject.class.equals(clazz));
+  }
+
+  public Object morph(Object bean) {
+    Map<Object, Object> result = new HashMap<Object, Object>();
+    JSONObject jsonObject = (JSONObject) bean;
+    for ( Object key : jsonObject.keySet()) {
+      result.put(key,jsonObject.get(key));
+    }
+    return result;
+  }
+
+}

Added: incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/JsonLibTestsGuiceModule.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/JsonLibTestsGuiceModule.java?rev=678158&view=auto
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/JsonLibTestsGuiceModule.java (added)
+++ incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/JsonLibTestsGuiceModule.java Sat Jul 19 06:51:03 2008
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.shindig.social;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.google.inject.AbstractModule;
+
+/**
+ * Provides social api component injection for all large tests
+ */
+public class JsonLibTestsGuiceModule extends AbstractModule {
+  @Override
+  protected void configure() {
+
+    bind(Map.class).to(HashMap.class);
+    bind(List.class).to(ArrayList.class);
+    bind(Map[].class).to(HashMap[].class);
+  }
+
+}

Added: incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/APIValidator.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/APIValidator.java?rev=678158&view=auto
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/APIValidator.java (added)
+++ incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/APIValidator.java Sat Jul 19 06:51:03 2008
@@ -0,0 +1,352 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.social.opensocial.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.EvaluatorException;
+import org.mozilla.javascript.Script;
+import org.mozilla.javascript.ScriptableObject;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+/**
+ * A class that loads a feature set from features into a Javascript Parser to
+ * make the model available to validate JSON messages against.
+ */
+public class APIValidator {
+
+  private static final Log log = LogFactory.getLog(APIValidator.class);
+  private Context ctx;
+  private ScriptableObject scope;
+
+  /**
+   * @param feature
+   *                The name of the feature are eg "opensocial-reference", this
+   *                is a classpath stub (not starting with /) where the location
+   *                contains a feature.xml file.
+   * @throws SAXException
+   *                 if feature.xml is not parsable
+   * @throws ParserConfigurationException
+   *                 if the parsers are invalid
+   * @throws IOException
+   *                 if feature.xml or the javascript that represents the
+   *                 feature is missing
+   * 
+   */
+  public APIValidator(String feature) throws IOException,
+      ParserConfigurationException, SAXException {
+    ctx = Context.enter();
+    scope = ctx.initStandardObjects();
+    load(feature);
+  }
+
+  /**
+   * @param json
+   *                The json to validate expected in a form { xyz: yyy } form
+   * @param object
+   *                The json Fields object specifying the structure of the json
+   *                object, each field in this object contains the name of the
+   *                json field in the json structure.
+   * @param optionalFields
+   *                If any of the fields that appear in the json structure are
+   *                optional, then they should be defined in this parameter.
+   * @param nullfields
+   * @throws APIValidatorExpcetion
+   *                 if there is a problem validating the json
+   * @return a map so string object pairs containing the fields at the top level
+   *         of the json tree. Where these are native java objects, they will
+   *         appear as native object. Complex json objects will appear as Rhino
+   *         specific objects
+   */
+  public Map<String, Object> validate(String json, String object,
+      String[] optionalFields, String[] nullfields)
+      throws APIValidatorExpcetion {
+
+    /*
+     * Object[] ids = ScriptableObject.getPropertyIds(scope); for (Object id :
+     * ids) { Object o = ScriptableObject.getProperty(scope,
+     * String.valueOf(id)); log.debug("ID is " + id + " class " + id.getClass() + "
+     * is " + o); if (o instanceof ScriptableObject) {
+     * listScriptable(String.valueOf(id), (ScriptableObject) o); } }
+     */
+
+    log.debug("Loading " + json);
+    json = json.trim();
+    if (!json.endsWith("}")) {
+      json = json + "}";
+    }
+    if (!json.startsWith("{")) {
+      json = "{" + json;
+    }
+    json = "( testingObject = " + json + " )";
+
+    Object so = null;
+    try {
+      so = ctx.evaluateString(scope, json, "test json", 0, null);
+    } catch (EvaluatorException ex) {
+      log.error("Non parseable JSON " + json);
+    }
+    log.debug("Loaded " + so);
+
+    return validateOject(so, object, optionalFields, nullfields);
+
+  }
+
+  /**
+   * Validate an JSON Object extracted
+   * 
+   * @param object
+   * @param string
+   * @param optional
+   * @return
+   * @throws APIValidatorExpcetion
+   */
+  public Map<String, Object> validateOject(Object jsonObject, String object,
+      String[] optionalFields, String[] nullFields)
+      throws APIValidatorExpcetion {
+    Map<String, String> optional = new HashMap<String, String>();
+    for (String opt : optionalFields) {
+      optional.put(opt, opt);
+    }
+    Map<String, String> nullf = new HashMap<String, String>();
+    for (String nf : nullFields) {
+      nullf.put(nf, nf);
+    }
+
+    ScriptableObject specification = getScriptableObject(object);
+    log.debug("Looking for  " + object + " found " + specification);
+    listScriptable(object, specification);
+
+    Map<String, Object> resultFields = new HashMap<String, Object>();
+
+    if (jsonObject instanceof ScriptableObject) {
+      ScriptableObject parsedJSONObject = (ScriptableObject) jsonObject;
+      listScriptable("testingObject", parsedJSONObject);
+      Object[] fields = specification.getIds();
+      for (Object f : fields) {
+        Object fieldName = specification.get(String.valueOf(f), specification);
+        Object o = parsedJSONObject.get(String.valueOf(fieldName),
+            parsedJSONObject);
+        if (o == ScriptableObject.NOT_FOUND) {
+          if (optional.containsKey(fieldName)) {
+            log.warn("Missing Optional Field " + fieldName);
+          } else if (!nullf.containsKey(fieldName)) {
+            log.error("Missing Field " + fieldName);
+            throw new APIValidatorExpcetion("Missing Field " + fieldName);
+          }
+        } else {
+          if (nullf.containsKey(fieldName)) {
+            log.error("Field should have been null and was not");
+          }
+          if (o == null) {
+            if (nullf.containsKey(fieldName)) {
+              log.error("Null Fields has been serialized " + fieldName);
+            }
+            log.debug("Got a Null object for Field " + f + ":" + fieldName
+                + " on json [[" + jsonObject + "]]");
+
+          } else {
+
+            log.debug("Got JSON Field  Field," + f + ":" + fieldName + " as "
+                + o + " " + o.getClass());
+          }
+          resultFields.put(String.valueOf(fieldName), o);
+        }
+      }
+
+    } else {
+      throw new APIValidatorExpcetion(
+          "Parsing JSON resulted in invalid Javascript object, which was "
+              + jsonObject + " JSON was [[" + jsonObject + "]]");
+    }
+    return resultFields;
+  }
+
+  /**
+   * get an object from the json context and scope.
+   * 
+   * @param object
+   *                the name of the object specified as a path from the base
+   *                object
+   * @return the json object
+   */
+  private ScriptableObject getScriptableObject(String object) {
+    String[] path = object.split("\\.");
+    log.debug("Looking up " + object + " elements " + path.length);
+
+    ScriptableObject s = scope;
+    for (String pe : path) {
+      log.debug("Looking up " + pe + " in " + s);
+      s = (ScriptableObject) s.get(pe, s);
+      log.debug("Looking for " + pe + " in found " + s);
+    }
+    return s;
+  }
+
+  /**
+   * List a scriptable object at log debug level, constructors will not be
+   * expanded as this loads to recursion.
+   * 
+   * @param id
+   *                The name of the object
+   * @param scriptableObject
+   *                the scriptable Object
+   */
+  private void listScriptable(String id, ScriptableObject scriptableObject) {
+    log.debug("ID is Scriptable " + id);
+    if (!id.endsWith("constructor")) {
+      Object[] allIDs = scriptableObject.getAllIds();
+      for (Object oid : allIDs) {
+        log.debug(id + "." + oid);
+        Object o = scriptableObject.get(String.valueOf(oid), scriptableObject);
+        if (o instanceof ScriptableObject) {
+          listScriptable(id + "." + String.valueOf(oid), (ScriptableObject) o);
+        }
+      }
+    }
+  }
+
+  /**
+   * Load a feature based on the spec
+   * 
+   * @param spec
+   *                The name of the location of the spec in the classpath, must
+   *                not start with a '/' and must should contain a feature.xml
+   *                file in the location
+   * @throws IOException
+   *                 If any of the resources cant be found
+   * @throws ParserConfigurationException
+   *                 If the parser has a problem being constructed
+   * @throws SAXException
+   *                 on a parse error on the features.xml
+   */
+  private void load(String spec) throws IOException, SAXException,
+      ParserConfigurationException {
+
+    List<String> scripts = getScripts(spec);
+
+    List<Script> compiled = new ArrayList<Script>();
+    for (String script : scripts) {
+      String scriptPath = spec + "/" + script;
+      InputStream in = this.getClass().getClassLoader().getResourceAsStream(
+          scriptPath);
+      if (in == null) {
+        in = this.getClass().getClassLoader().getResourceAsStream(
+            "features/" + scriptPath);
+        if (in == null) {
+          throw new IOException("Cant load spec " + spec + " or features/"+spec+" from classpath");
+        }
+      }
+      InputStreamReader reader = new InputStreamReader(in);
+      Script compiledScript = ctx.compileReader(reader, spec, 0, null);
+      compiled.add(compiledScript);
+    }
+
+    for (Script compiledScript : compiled) {
+      compiledScript.exec(ctx, scope);
+    }
+
+  }
+
+  /**
+   * Add some javascript to the context, and execute it. If extra custom
+   * javascript is wanted in the context or scope then this method will load it.
+   * 
+   * @param javascript
+   */
+  public void addScript(String javascript) {
+    Script compileScript = ctx.compileString(javascript, "AdditionalJS", 0,
+        null);
+    compileScript.exec(ctx, scope);
+  }
+
+  /**
+   * Get an ordered list of javascript resources from a feature sets.
+   * 
+   * @param spec
+   *                The spec location
+   * @return An ordered list of javascript resources, these are relative to
+   *         specification file.
+   * @throws IOException
+   *                 If any of the resources can't be loaded.
+   * @throws SAXException
+   *                 Where the feature.xml file is not parsable
+   * @throws ParserConfigurationException
+   *                 where the parser can't be constructed.
+   * @return An ordered list of script that need to be loaded and executed to
+   *         make the feature available in the context.
+   */
+  private List<String> getScripts(String spec) throws SAXException,
+      IOException, ParserConfigurationException {
+    String features = spec + "/feature.xml";
+    InputStream in = this.getClass().getClassLoader().getResourceAsStream(
+        features);
+    if (in == null) {
+      in = this.getClass().getClassLoader().getResourceAsStream(
+          "features/"+features);
+      if (in == null) {
+        throw new IOException("Cant find " + features + " or features/" + features+ " in classpath ");
+      }
+    }
+    DocumentBuilderFactory builderFactory = DocumentBuilderFactory
+        .newInstance();
+    DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder();
+    Document doc = documentBuilder.parse(in);
+    NodeList nl = doc.getElementsByTagName("script");
+    List<String> scripts = new ArrayList<String>();
+    for (int i = 0; i < nl.getLength(); i++) {
+      Node scriptNode = nl.item(i);
+      NamedNodeMap attributes = scriptNode.getAttributes();
+      Node scriptAttr = attributes.getNamedItem("src");
+      String script = scriptAttr.getNodeValue();
+      scripts.add(script);
+    }
+    return scripts;
+  }
+
+  /**
+   * @param nameJSON
+   */
+  public static void dump(Map<?, ?> nameJSON) {
+    if (log.isDebugEnabled()) {
+      for (Object k : nameJSON.keySet()) {
+        Object o = nameJSON.get(k);
+        log.info("Key [" + k + "] value:["
+            + (o == null ? o : o + ":" + o.getClass()) + "]");
+      }
+    }
+  }
+
+}

Added: incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/APIValidatorExpcetion.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/APIValidatorExpcetion.java?rev=678158&view=auto
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/APIValidatorExpcetion.java (added)
+++ incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/APIValidatorExpcetion.java Sat Jul 19 06:51:03 2008
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.social.opensocial.util;
+
+/**
+ * Thrown when there is a validation exception on one of the apis
+ */
+public class APIValidatorExpcetion extends Exception {
+
+  private static final long serialVersionUID = -8969858718669454612L;
+
+  public APIValidatorExpcetion() {
+  }
+
+  public APIValidatorExpcetion(String message) {
+    super(message);
+  }
+
+  public APIValidatorExpcetion(Throwable cause) {
+    super(cause);
+  }
+  
+  public APIValidatorExpcetion(String message, Throwable cause) {
+    super(message, cause);
+  }
+
+}

Added: incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/APIValidatorTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/APIValidatorTest.java?rev=678158&view=auto
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/APIValidatorTest.java (added)
+++ incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/APIValidatorTest.java Sat Jul 19 06:51:03 2008
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.social.opensocial.util;
+
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.xml.sax.SAXException;
+
+/**
+ * Test the api validator to make certain it behaves as is expected.
+ */
+public class APIValidatorTest {
+
+  /**
+   * A definition of the test json used in this unit test.
+   */
+  private static final String TEST_DEFINITION = 
+    "var TestDef = {}; TestDef.Field = { FIELD1 : \"json\", FIELD2 : \"xyz\", FIELD3 : \"shouldBeMissing\" };";
+
+  /**
+   * test the validator for successful validation
+   * @throws APIValidatorExpcetion
+   * @throws IOException
+   * @throws ParserConfigurationException
+   * @throws SAXException
+   */
+  @Test
+  public void testValidator() throws APIValidatorExpcetion, IOException, ParserConfigurationException, SAXException {
+    APIValidator apiVal = new APIValidator("opensocial-reference");
+    apiVal.addScript(TEST_DEFINITION);
+    String[] optional = {"shouldBeMissing"};
+    String[] nullfields = {};
+     Map<String, Object> result = apiVal.validate("{ json: \"A Test JSON\", xyz : 123 }", "TestDef.Field", optional, nullfields );
+     Assert.assertNotNull(result);
+     Assert.assertNotNull(result.get("json"));
+     Assert.assertNotNull(result.get("xyz"));
+     Assert.assertEquals(String.class,result.get("json").getClass());
+     Assert.assertEquals(Integer.class,result.get("xyz").getClass());
+     Assert.assertEquals("A Test JSON",result.get("json"));
+     Assert.assertEquals(123,((Integer)result.get("xyz")).intValue());
+     
+    
+  }
+  
+  /**
+   * Test for a failing validation
+   * @throws APIValidatorExpcetion
+   * @throws IOException
+   * @throws ParserConfigurationException
+   * @throws SAXException
+   */
+  @Test
+  public void testValidatorFail() throws APIValidatorExpcetion, IOException, ParserConfigurationException, SAXException {
+    APIValidator apiVal = new APIValidator("opensocial-reference");
+    apiVal.addScript(TEST_DEFINITION);
+    String[] optional = {};
+    String[] nullfields = {};
+    try {
+      apiVal.validate("{ jsonIsMissing: \"A Test JSON\", xyz : 123 }", "TestDef.Field", optional, nullfields );
+      Assert.fail("Should have Generated an APIValidatorException ");
+    } catch ( APIValidatorExpcetion ex ) {
+      
+    }
+     
+    
+  }
+
+}

Added: incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/BeanJsonLibConverterTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/BeanJsonLibConverterTest.java?rev=678158&view=auto
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/BeanJsonLibConverterTest.java (added)
+++ incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/BeanJsonLibConverterTest.java Sat Jul 19 06:51:03 2008
@@ -0,0 +1,359 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.social.opensocial.util;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shindig.social.JsonLibTestsGuiceModule;
+import org.apache.shindig.social.opensocial.model.Activity;
+import org.apache.shindig.social.opensocial.model.ActivityImpl;
+import org.apache.shindig.social.opensocial.model.Address;
+import org.apache.shindig.social.opensocial.model.AddressImpl;
+import org.apache.shindig.social.opensocial.model.Email;
+import org.apache.shindig.social.opensocial.model.EmailImpl;
+import org.apache.shindig.social.opensocial.model.MediaItem;
+import org.apache.shindig.social.opensocial.model.MediaItemImpl;
+import org.apache.shindig.social.opensocial.model.Name;
+import org.apache.shindig.social.opensocial.model.NameImpl;
+import org.apache.shindig.social.opensocial.model.Person;
+import org.apache.shindig.social.opensocial.model.PersonImpl;
+import org.apache.shindig.social.opensocial.model.Phone;
+import org.apache.shindig.social.opensocial.model.PhoneImpl;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.inject.Guice;
+
+public class BeanJsonLibConverterTest extends TestCase {
+
+  private static final Log log = LogFactory
+      .getLog(BeanJsonLibConverterTest.class);
+  private Person johnDoe;
+  private Activity activity;
+
+  private BeanJsonLibConverter beanJsonConverter;
+  private APIValidator apiValidator;
+  // set to true to get loging output at info level
+  private boolean outputInfo = false;
+
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+    johnDoe = new PersonImpl("johnDoeId", new NameImpl("John Doe"));
+    johnDoe.setPhoneNumbers(Lists.<Phone> newArrayList(new PhoneImpl(
+        "+33H000000000", "home"), new PhoneImpl("+33M000000000", "mobile"),
+        new PhoneImpl("+33W000000000", "work")));
+
+    johnDoe.setAddresses(Lists.<Address> newArrayList(new AddressImpl(
+        "My home address")));
+
+    johnDoe.setEmails(Lists.<Email> newArrayList(new EmailImpl(
+        "john.doe@work.bar", "work"),
+        new EmailImpl("john.doe@home.bar", "home")));
+
+    activity = new ActivityImpl("activityId", johnDoe.getId());
+
+    activity.setMediaItems(Lists.<MediaItem> newArrayList(new MediaItemImpl(
+        "image/jpg", MediaItem.Type.IMAGE, "http://foo.bar")));
+
+    beanJsonConverter = new BeanJsonLibConverter(Guice
+        .createInjector(new JsonLibTestsGuiceModule()));
+
+    apiValidator = new APIValidator("opensocial-reference");
+
+  }
+
+  public static class SpecialPerson extends PersonImpl {
+    public static final String[] OPTIONALFIELDS = {};
+    public static final String[] NULLFIELDS  = {"jobInterests","nickname","romance","religion","timeZone",
+    "relationshipStatus","tags","networkPresence","books","quotes","phoneNumbers","languagesSpoken",
+    "activities","jobs","dateOfBirth","profileVideo","bodyType","urls","schools","music","addresses",
+    "livingArrangement","thumbnailUrl","humor","sports","scaredOf","movies","age","pets","hasApp","turnOffs",
+    "gender","fashion","drinker","aboutMe","children","sexualOrientation","heroes","profileSong","lookingFor",
+    "cars","turnOns","tvShows","profileUrl","status","currentLocation","smoker","happiestWhen","ethnicity",
+    "food","emails","politicalViews","interests","familyName","honorificSuffix","additionalName","honorificPrefix","givenName"};
+
+    private String newfield;
+
+    public SpecialPerson() {
+      super();
+    }
+
+    public SpecialPerson(String id, String name, String newfield) {
+      super(id, new NameImpl(name));
+      this.newfield = newfield;
+    }
+
+    public String getNewfield() {
+      return newfield;
+    }
+
+    public void setNewfield(String newfield) {
+      this.newfield = newfield;
+    }
+
+  }
+
+  public void testToJsonOnInheritedClass() throws Exception {
+    SpecialPerson cassie = new SpecialPerson("5", "robot", "nonsense");
+
+    String result = beanJsonConverter.convertToString(cassie);
+
+    validatePerson(result, "5", "robot", SpecialPerson.OPTIONALFIELDS, SpecialPerson.NULLFIELDS);
+
+    apiValidator.addScript(" specialPerson = { SPECIAL : \"newfield\" }; ");
+    String[] optional = {};
+    String[] nullfields = {};
+    Map<String, Object> special = apiValidator.validate(result,
+        "specialPerson", optional,nullfields);
+    assertNotNull(special.get("newfield"));
+    assertEquals(String.class, special.get("newfield").getClass());
+    assertEquals("nonsense", special.get("newfield"));
+
+    // convert back into an object Tree
+
+    SpecialPerson parseCassie = beanJsonConverter.convertToObject(result,
+        SpecialPerson.class);
+
+    assertNotNull(parseCassie);
+    assertEquals(cassie.getId(), parseCassie.getId());
+    assertEquals(cassie.getNewfield(), parseCassie.getNewfield());
+
+    Name name = parseCassie.getName();
+    Name cassieName = cassie.getName();
+    assertNotNull(name);
+    assertEquals(cassieName.getUnstructured(), name.getUnstructured());
+    assertEquals(cassieName.getAdditionalName(), name.getAdditionalName());
+    assertEquals(cassieName.getFamilyName(), name.getFamilyName());
+    assertEquals(cassieName.getGivenName(), name.getGivenName());
+    assertEquals(cassieName.getHonorificPrefix(), name.getHonorificPrefix());
+    assertEquals(cassieName.getHonorificSuffix(), name.getHonorificSuffix());
+
+  }
+
+  /**
+   * @param result
+   * @throws APIValidatorExpcetion
+   */
+  private void validatePerson(String result, String id, String name, String[] optional, String[] nullfields)
+      throws APIValidatorExpcetion {
+
+    Map<String, Object> standard = apiValidator.validate(result,
+        "opensocial.Person.Field", optional,nullfields);
+    assertNotNull(standard.get("id"));
+    assertEquals(String.class, standard.get("id").getClass());
+    assertEquals(id, standard.get("id"));
+
+    assertNotNull(standard.get("name"));
+    Map<String, Object> nameJSON = apiValidator.validateOject(standard
+        .get("name"), "opensocial.Name.Field", optional,nullfields);
+    APIValidator.dump(nameJSON);
+
+    assertNotNull(nameJSON.get("unstructured"));
+    assertEquals(String.class, nameJSON.get("unstructured").getClass());
+    assertEquals(name, nameJSON.get("unstructured"));
+
+    // additional name
+    assertNull(nameJSON.get("additionalName"));
+
+  }
+
+  public void testPersonToJson() throws Exception {
+    String result = beanJsonConverter.convertToString(johnDoe);
+    if (outputInfo) {
+      log.info("JSON (" + result + ")");
+    }
+    Person parsedPerson = beanJsonConverter.convertToObject(result,
+        Person.class);
+
+    assertEquals(johnDoe.getId(), parsedPerson.getId());
+    assertEquals(johnDoe.getName().getUnstructured(), parsedPerson.getName()
+        .getUnstructured());
+
+    List<Address> addresses = parsedPerson.getAddresses();
+    if (outputInfo) {
+      for (Object o : addresses) {
+        log.info("Address " + o);
+      }
+    }
+
+    assertEquals(1, addresses.size());
+    Address address = addresses.get(0);
+    String unstructured = address.getUnstructuredAddress();
+
+    assertNotNull(unstructured);
+    assertEquals(johnDoe.getAddresses().get(0).getUnstructuredAddress(),
+        parsedPerson.getAddresses().get(0).getUnstructuredAddress());
+
+    assertEquals(3, parsedPerson.getPhoneNumbers().size());
+
+    for (int i = 0; i < johnDoe.getPhoneNumbers().size(); i++) {
+      Phone expectedPhone = johnDoe.getPhoneNumbers().get(i);
+      Phone actualPhone = parsedPerson.getPhoneNumbers().get(i);
+      assertEquals(expectedPhone.getType(), actualPhone.getType());
+      assertEquals(expectedPhone.getNumber(), actualPhone.getNumber());
+    }
+
+    assertEquals(2, parsedPerson.getEmails().size());
+
+    for (int i = 0; i < johnDoe.getEmails().size(); i++) {
+      Email expectedEmail = johnDoe.getEmails().get(i);
+      Email actualEmail = parsedPerson.getEmails().get(i);
+      assertEquals(expectedEmail.getType(), actualEmail.getType());
+      assertEquals(expectedEmail.getAddress(), actualEmail.getAddress());
+    }
+  }
+
+  public void testActivityToJson() throws Exception {
+
+    String result = beanJsonConverter.convertToString(activity);
+    if (outputInfo) {
+      log.info("JSON (" + result + ")");
+    }
+    Activity parsedActivity = beanJsonConverter.convertToObject(result,
+        Activity.class);
+    assertEquals(activity.getUserId(), parsedActivity.getUserId());
+    assertEquals(activity.getId(), parsedActivity.getId());
+
+    assertEquals(1, parsedActivity.getMediaItems().size());
+
+    MediaItem expectedItem = activity.getMediaItems().get(0);
+    MediaItem actualItem = parsedActivity.getMediaItems().get(0);
+
+    assertEquals(expectedItem.getUrl(), actualItem.getUrl());
+    assertEquals(expectedItem.getMimeType(), actualItem.getMimeType());
+    assertEquals(expectedItem.getType().toString(), actualItem.getType()
+        .toString());
+  }
+
+  public void testMapsToJson() throws Exception {
+    Map<String, Map<String, String>> map = Maps.newHashMap();
+
+    Map<String, String> item1Map = Maps.newHashMap();
+    item1Map.put("value", "1");
+
+    // Null values shouldn't cause exceptions
+    item1Map.put("value2", null);
+    map.put("item1", item1Map);
+
+    Map<String, String> item2Map = Maps.newHashMap();
+    item2Map.put("value", "2");
+    map.put("item2", item2Map);
+
+    String result = beanJsonConverter.convertToString(map);
+    if (outputInfo) {
+      log.info("JSON (" + result + ")");
+    }
+    // there is introspection that can tell jsonobject -> bean converter what a
+    // map should contain, so we have to tell it
+    beanJsonConverter.addMapping("item1", Map.class);
+    beanJsonConverter.addMapping("item2", Map.class);
+    Map<?, ?> parsedMap = beanJsonConverter
+        .convertToObject(result, Map.class);
+
+    if (outputInfo) {
+      log.info("Dumping Map (" + parsedMap + ")");
+    }
+    APIValidator.dump(parsedMap);
+
+    assertEquals("1", ((Map<?,?>)parsedMap.get("item1")).get("value"));
+    assertEquals("2", ((Map<?,?>)parsedMap.get("item2")).get("value"));
+  }
+
+  public void testListsToJson() throws Exception {
+    Map<String, String> item1Map = Maps.newHashMap();
+    item1Map.put("value", "1");
+
+    Map<String, String> item2Map = Maps.newHashMap();
+    item2Map.put("value", "2");
+
+    // put the list into a container before serializing, top level lists dont
+    // appear
+    // to be allowed in json
+    // just check that the list is in the holder correctly
+    List<Map<String, String>> list = new ArrayList<Map<String, String>>();
+    list.add(item1Map);
+    list.add(item2Map);
+    String result = beanJsonConverter.convertToString(list);
+    if (outputInfo) {
+      log.info("JSON (" + result + ")");
+    }
+    Map<?, ?>[] parsedList = beanJsonConverter.convertToObject(
+        result, Map[].class);
+
+    assertEquals("1", parsedList[0].get("value"));
+    assertEquals("2", parsedList[1].get("value"));
+  }
+
+  public void testArrayToJson() throws Exception {
+    String[] colors = { "blue", "green", "aquamarine" };
+    String result = beanJsonConverter.convertToString(colors);
+    if (outputInfo) {
+      log.info("JSON (" + result + ")");
+    }
+    String[] parsedColors = beanJsonConverter.convertToObject(result,
+        String[].class);
+    assertEquals(colors.length, parsedColors.length);
+    assertEquals(colors[0], parsedColors[0]);
+    assertEquals(colors[1], parsedColors[1]);
+    assertEquals(colors[2], parsedColors[2]);
+  }
+
+  public void testJsonToActivity() throws Exception {
+    String jsonActivity = "{userId : 5, id : 6, mediaItems : ["
+        + "{url : 'hello', mimeType : 'mimey', type : 'VIDEO'}" + "]}";
+    Activity result = beanJsonConverter.convertToObject(jsonActivity,
+        Activity.class);
+
+    assertEquals("5", result.getUserId());
+    assertEquals("6", result.getId());
+
+    assertEquals(1, result.getMediaItems().size());
+
+    MediaItem actualItem = result.getMediaItems().get(0);
+
+    assertEquals("hello", actualItem.getUrl());
+    assertEquals("mimey", actualItem.getMimeType());
+    assertEquals("video", actualItem.getType().toString());
+  }
+
+  public void testJsonToMap() throws Exception {
+    String jsonActivity = "{count : 0, favoriteColor : 'yellow'}";
+    Map<String, String> data = Maps.newHashMap();
+    data = beanJsonConverter.convertToObject(jsonActivity,
+        (Class<Map<String, String>>) data.getClass());
+
+    assertEquals(2, data.size());
+
+    for (String key : data.keySet()) {
+      Object value = data.get(key);
+      if (key.equals("count")) {
+        assertEquals(0, value);
+      } else if (key.equals("favoriteColor")) {
+        assertEquals("yellow", value);
+      }
+    }
+  }
+
+}

Added: incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/JsonConverterPerformancePerf.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/JsonConverterPerformancePerf.java?rev=678158&view=auto
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/JsonConverterPerformancePerf.java (added)
+++ incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/util/JsonConverterPerformancePerf.java Sat Jul 19 06:51:03 2008
@@ -0,0 +1,271 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.social.opensocial.util;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shindig.social.JsonLibTestsGuiceModule;
+import org.apache.shindig.social.SocialApiTestsGuiceModule;
+import org.apache.shindig.social.opensocial.model.Activity;
+import org.apache.shindig.social.opensocial.model.ActivityImpl;
+import org.apache.shindig.social.opensocial.model.Address;
+import org.apache.shindig.social.opensocial.model.AddressImpl;
+import org.apache.shindig.social.opensocial.model.Email;
+import org.apache.shindig.social.opensocial.model.EmailImpl;
+import org.apache.shindig.social.opensocial.model.MediaItem;
+import org.apache.shindig.social.opensocial.model.MediaItemImpl;
+import org.apache.shindig.social.opensocial.model.NameImpl;
+import org.apache.shindig.social.opensocial.model.Person;
+import org.apache.shindig.social.opensocial.model.PersonImpl;
+import org.apache.shindig.social.opensocial.model.Phone;
+import org.apache.shindig.social.opensocial.model.PhoneImpl;
+import org.json.JSONObject;
+
+import com.google.common.collect.Lists;
+import com.google.inject.Guice;
+
+public class JsonConverterPerformancePerf extends TestCase {
+
+  private static final Log log = LogFactory
+      .getLog(JsonConverterPerformancePerf.class);
+  private static final int TEST_SIZE = 10000;
+  private Person johnDoe;
+  private Activity activity;
+
+  private BeanJsonLibConverter beanJsonLibConverter;
+  private APIValidator apiValidator;
+  // set to true to get loging output at info level
+  private boolean outputInfo = false;
+  private BeanJsonConverter beanJsonConverter;
+
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+    johnDoe = new PersonImpl("johnDoeId", new NameImpl("John Doe"));
+    johnDoe.setPhoneNumbers(Lists.<Phone> newArrayList(new PhoneImpl(
+        "+33H000000000", "home"), new PhoneImpl("+33M000000000", "mobile"),
+        new PhoneImpl("+33W000000000", "work")));
+
+    johnDoe.setAddresses(Lists.<Address> newArrayList(new AddressImpl(
+        "My home address")));
+
+    johnDoe.setEmails(Lists.<Email> newArrayList(new EmailImpl(
+        "john.doe@work.bar", "work"),
+        new EmailImpl("john.doe@home.bar", "home")));
+
+    activity = new ActivityImpl("activityId", johnDoe.getId());
+
+    activity.setMediaItems(Lists.<MediaItem> newArrayList(new MediaItemImpl(
+        "image/jpg", MediaItem.Type.IMAGE, "http://foo.bar")));
+
+    beanJsonLibConverter = new BeanJsonLibConverter(Guice
+        .createInjector(new JsonLibTestsGuiceModule()));
+
+    beanJsonConverter = new BeanJsonConverter(Guice
+        .createInjector(new SocialApiTestsGuiceModule()));
+
+    apiValidator = new APIValidator("opensocial-reference");
+
+  }
+
+  public static class SpecialPerson extends PersonImpl {
+    private String newfield;
+
+    public SpecialPerson() {
+      super();
+    }
+
+    public SpecialPerson(String id, String name, String newfield) {
+      super(id, new NameImpl(name));
+      this.newfield = newfield;
+    }
+
+    public String getNewfield() {
+      return newfield;
+    }
+
+    public void setNewfield(String newfield) {
+      this.newfield = newfield;
+    }
+
+  }
+
+  public void testToJsonLibOnInheritedClassOutput() throws Exception {
+    SpecialPerson[] spa = new SpecialPerson[TEST_SIZE];
+    for (int i = 0; i < TEST_SIZE; i++) {
+      spa[i] = new SpecialPerson(String.valueOf(i), "robot", "nonsense");
+    }
+    Runtime r = Runtime.getRuntime();
+    r.gc();
+    long memstart = r.totalMemory()-r.freeMemory();
+    long startOutput = System.currentTimeMillis();
+    String[] output = new String[TEST_SIZE];
+    for (int i = 0; i < TEST_SIZE; i++) {
+      output[i] = beanJsonLibConverter.convertToString(spa[i]);
+    }
+    long endOutput = System.currentTimeMillis();
+    long memend = r.totalMemory()-r.freeMemory();
+
+    String[] serializeOutput = new String[TEST_SIZE];
+    char[] source = output[0].toCharArray();
+    r.gc();
+
+    long stringsizeStart = r.totalMemory()-r.freeMemory();
+    for (int i = 0; i < TEST_SIZE; i++) {
+      serializeOutput[i] = new String(source);
+    }
+    long stringsizeEnd = r.totalMemory()-r.freeMemory();
+
+    /*
+     * Output the time per conversion and the memory usage - the output per
+     * conversion.
+     * 
+     */
+
+    log.info("SF JSON Lib Output "
+        + average(startOutput, endOutput, TEST_SIZE)
+        + " ms/conversion, "
+        + (average(memstart, memend, TEST_SIZE)-average(stringsizeStart, stringsizeEnd, TEST_SIZE))
+        + " heap bytes/conversion, output packet consumed on average "
+        + average(stringsizeStart, stringsizeEnd, TEST_SIZE)+" for a string length of "+output[0].length());
+    log.info("Output Was ["+output[0]+"]");
+  }
+
+  public void testToJsonLibOnInheritedClassInput() throws Exception {
+    SpecialPerson[] spa = new SpecialPerson[TEST_SIZE];
+    SpecialPerson[] sparesult = new SpecialPerson[TEST_SIZE];
+    Runtime r = Runtime.getRuntime();
+    r.gc();
+    long personStart = r.totalMemory()-r.freeMemory();
+    for (int i = 0; i < TEST_SIZE; i++) {
+      spa[i] = new SpecialPerson(String.valueOf(i), "robot", "nonsense");
+    }
+    long personEnd = r.totalMemory()-r.freeMemory();
+
+    String[] serializeOutput = new String[TEST_SIZE];
+    r.gc();
+    for (int i = 0; i < TEST_SIZE; i++) {
+
+      serializeOutput[i] = beanJsonLibConverter.convertToString(spa[i]);
+    }
+
+    r.gc();
+    long memstart = r.totalMemory()-r.freeMemory();
+    long startInput = System.currentTimeMillis();
+    for (int i = 0; i < TEST_SIZE; i++) {
+        sparesult[i] = beanJsonLibConverter.convertToObject(
+          serializeOutput[i], SpecialPerson.class);
+    }
+    long endInput = System.currentTimeMillis();
+    long memend = r.totalMemory()-r.freeMemory();
+
+    log.info("SF JSON Lib Input "
+        + average(startInput, endInput, TEST_SIZE)
+        + " ms/conversion, "
+        + (average(memstart, memend, TEST_SIZE)-average(personStart, personEnd, TEST_SIZE))
+        + " heap bytes/conversion, person object consumed on average "
+        + average(personStart, personEnd, TEST_SIZE));
+  }
+
+  public void testToJsonOnInheritedClassOutput() throws Exception {
+    SpecialPerson[] spa = new SpecialPerson[TEST_SIZE];
+    for (int i = 0; i < TEST_SIZE; i++) {
+      spa[i] = new SpecialPerson(String.valueOf(i), "robot", "nonsense");
+    }
+    Runtime r = Runtime.getRuntime();
+    String[] output = new String[TEST_SIZE];
+    r.gc();
+    long memstart = r.totalMemory()-r.freeMemory();
+    long startOutput = System.currentTimeMillis();
+    for (int i = 0; i < TEST_SIZE; i++) {
+      output[i]  = ((JSONObject) beanJsonConverter.convertToJson(spa[i])).toString();
+    }
+    long endOutput = System.currentTimeMillis();
+    long memend = r.totalMemory()-r.freeMemory();
+    String[] serializeOutput = new String[TEST_SIZE];
+    char[] source = output[0].toCharArray();
+    r.gc();
+
+    long stringsizeStart = r.totalMemory()-r.freeMemory();
+    
+    for (int i = 0; i < TEST_SIZE; i++) {
+      serializeOutput[i] = new String(source);
+    }
+    long stringsizeEnd = r.totalMemory()-r.freeMemory();
+
+    log.info("ORG JSON Lib Output "
+        + average(startOutput, endOutput, TEST_SIZE)
+        + " ms/conversion, "
+        + (average(memstart, memend, TEST_SIZE)-average(stringsizeStart, stringsizeEnd, TEST_SIZE))
+        + " heap bytes/conversion, output packet consumed on average "
+        + average(stringsizeStart, stringsizeEnd, TEST_SIZE)+" for a string length of "+output[0].length());
+    log.info("Output Was ["+output[0]+"]");
+  }
+
+  /**
+   * @param endOutput
+   * @param startOutput
+   * @param testSize
+   * @return
+   */
+  private float average(long start, long end, int testSize) {
+    float r = end - start;
+    r = r / testSize;
+    return r;
+  }
+
+  public void XtestToJsonOnInheritedClassInput() throws Exception {
+    SpecialPerson[] spa = new SpecialPerson[TEST_SIZE];
+    SpecialPerson[] sparesult = new SpecialPerson[TEST_SIZE];
+    Runtime r = Runtime.getRuntime();
+    r.gc();
+    long personStart = r.totalMemory()-r.freeMemory();
+    for (int i = 0; i < TEST_SIZE; i++) {
+      spa[i] = new SpecialPerson(String.valueOf(i), "robot", "nonsense");
+    }
+    long personEnd = r.totalMemory()-r.freeMemory();
+
+    String[] serializeOutput = new String[TEST_SIZE];
+    r.gc();
+    for (int i = 0; i < TEST_SIZE; i++) {
+
+      serializeOutput[i] = beanJsonConverter.convertToString(spa[i]);
+    }
+
+    r.gc();
+    long memstart = r.totalMemory()-r.freeMemory();
+    long startInput = System.currentTimeMillis();
+
+    for (int i = 0; i < TEST_SIZE; i++) {
+      sparesult[i] = beanJsonConverter.convertToObject(
+          serializeOutput[i], SpecialPerson.class);
+    }
+    long endInput = System.currentTimeMillis();
+    long memend = r.totalMemory()-r.freeMemory();
+
+    log.info("SF JSON Lib Input "
+        + average(startInput, endInput, TEST_SIZE)
+        + " ms/conversion, "
+        + (average(memstart, memend, TEST_SIZE)-average(personStart, personEnd, TEST_SIZE))
+        + " heap bytes/conversion, person object consumed on average "
+        + average(personStart, personEnd, TEST_SIZE));
+  }
+
+}

Modified: incubator/shindig/trunk/pom.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/pom.xml?rev=678158&r1=678157&r2=678158&view=diff
==============================================================================
--- incubator/shindig/trunk/pom.xml (original)
+++ incubator/shindig/trunk/pom.xml Sat Jul 19 06:51:03 2008
@@ -853,6 +853,16 @@
         <artifactId>htmlunit</artifactId>
         <version>2.1</version>
       </dependency>
+      <dependency>
+        <groupId>log4j</groupId>
+        <artifactId>log4j</artifactId>
+        <version>1.2.13</version>
+      </dependency>
+      <dependency>
+        <groupId>rhino</groupId>
+        <artifactId>js</artifactId>
+        <version>1.6R7</version>
+      </dependency>
     </dependencies>
   </dependencyManagement>
 </project>