You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rave.apache.org by jc...@apache.org on 2011/10/11 22:19:41 UTC

svn commit: r1182068 - in /incubator/rave/trunk: rave-components/rave-commons/src/main/java/org/apache/rave/exception/ rave-components/rave-commons/src/main/java/org/apache/rave/persistence/ rave-components/rave-commons/src/main/java/org/apache/rave/pe...

Author: jcian
Date: Tue Oct 11 20:19:40 2011
New Revision: 1182068

URL: http://svn.apache.org/viewvc?rev=1182068&view=rev
Log:
RAVE-34: Implement AppData

https://issues.apache.org/jira/browse/RAVE-34

Added:
    incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/exception/DataSerializationException.java
    incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/model/ApplicationData.java
    incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/repository/ApplicationDataRepository.java
    incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/repository/impl/JpaApplicationDataRepository.java
    incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/service/SimplePersonService.java
    incubator/rave/trunk/rave-shindig/src/test/java/org/apache/rave/opensocial/repository/JpaApplicationDataRepositoryTest.java
Modified:
    incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/persistence/Repository.java
    incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/persistence/jpa/AbstractJpaRepository.java
    incubator/rave/trunk/rave-components/rave-commons/src/test/java/org/apache/rave/persistence/jpa/AbstractJpaRepositoryTest.java
    incubator/rave/trunk/rave-shindig/pom.xml
    incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/service/impl/DefaultAppDataService.java
    incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/service/impl/DefaultPersonService.java
    incubator/rave/trunk/rave-shindig/src/main/resources/META-INF/persistence.xml
    incubator/rave/trunk/rave-shindig/src/main/resources/initial_data.sql
    incubator/rave/trunk/rave-shindig/src/main/resources/rave-shindig-applicationContext.xml
    incubator/rave/trunk/rave-shindig/src/test/java/org/apache/rave/opensocial/service/AppDataServiceTest.java
    incubator/rave/trunk/rave-shindig/src/test/resources/test_data.sql

Added: incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/exception/DataSerializationException.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/exception/DataSerializationException.java?rev=1182068&view=auto
==============================================================================
--- incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/exception/DataSerializationException.java (added)
+++ incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/exception/DataSerializationException.java Tue Oct 11 20:19:40 2011
@@ -0,0 +1,36 @@
+/*
+ * 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.rave.exception;
+
+public class DataSerializationException extends RuntimeException {
+    public DataSerializationException() {
+    }
+
+    public DataSerializationException(String message) {
+        super(message);
+    }
+
+    public DataSerializationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public DataSerializationException(Throwable cause) {
+        super(cause);
+    }
+}

Modified: incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/persistence/Repository.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/persistence/Repository.java?rev=1182068&r1=1182067&r2=1182068&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/persistence/Repository.java (original)
+++ incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/persistence/Repository.java Tue Oct 11 20:19:40 2011
@@ -27,7 +27,7 @@ public interface Repository<T> {
      * Gets the type of the object served by the repository
      * @return a {@link Class} representing the type of object
      */
-    Class<T> getType();
+    Class<? extends T> getType();
 
     /**
      * Gets the specified object from the persistence context by its id

Modified: incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/persistence/jpa/AbstractJpaRepository.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/persistence/jpa/AbstractJpaRepository.java?rev=1182068&r1=1182067&r2=1182068&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/persistence/jpa/AbstractJpaRepository.java (original)
+++ incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/persistence/jpa/AbstractJpaRepository.java Tue Oct 11 20:19:40 2011
@@ -36,14 +36,14 @@ public abstract class AbstractJpaReposit
     @PersistenceContext
     protected EntityManager manager;
 
-    private final Class<T> type;
+    private final Class<? extends T> type;
 
-    protected AbstractJpaRepository(Class<T> type) {
+    protected AbstractJpaRepository(Class<? extends T> type) {
         this.type = type;
     }
 
     @Override
-    public Class<T> getType() {
+    public Class<? extends T> getType() {
         return type;
     }
 

Modified: incubator/rave/trunk/rave-components/rave-commons/src/test/java/org/apache/rave/persistence/jpa/AbstractJpaRepositoryTest.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-commons/src/test/java/org/apache/rave/persistence/jpa/AbstractJpaRepositoryTest.java?rev=1182068&r1=1182067&r2=1182068&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-commons/src/test/java/org/apache/rave/persistence/jpa/AbstractJpaRepositoryTest.java (original)
+++ incubator/rave/trunk/rave-components/rave-commons/src/test/java/org/apache/rave/persistence/jpa/AbstractJpaRepositoryTest.java Tue Oct 11 20:19:40 2011
@@ -53,7 +53,7 @@ public class AbstractJpaRepositoryTest {
 
     @Test
     public void getType() {
-        assertThat(repository.getType(), is(equalTo(TestEntity.class)));
+        assertThat((Class<TestEntity>) repository.getType(), is(equalTo(TestEntity.class)));
     }
 
     @Test

Modified: incubator/rave/trunk/rave-shindig/pom.xml
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-shindig/pom.xml?rev=1182068&r1=1182067&r2=1182068&view=diff
==============================================================================
--- incubator/rave/trunk/rave-shindig/pom.xml (original)
+++ incubator/rave/trunk/rave-shindig/pom.xml Tue Oct 11 20:19:40 2011
@@ -157,8 +157,10 @@
                 <artifactId>openjpa-maven-plugin</artifactId>
                 <version>1.2</version>
                 <configuration>
-                    <includes>org/apache/rave/gadgets/oauth/model/*.class,
+                    <includes>
+                        org/apache/rave/gadgets/oauth/model/*.class,
                         org/apache/rave/opensocial/model/*.class,
+                        org/apache/rave/opensocial/repository/impl/JpaApplicationDataRepository$JpaSerializableApplicationData.class,
                         org/apache/shindig/social/opensocial/jpa/*.class
                     </includes>
                     <excludes>
@@ -201,8 +203,7 @@
                         </mapping>
                         <mapping>
                             <sourceFolder>src/main/resources</sourceFolder>
-                            <destinationFolder>@../rave-portal/target/tomcat6x/webapps/ROOT/WEB-INF/classes
-                            </destinationFolder>
+                            <destinationFolder>@../rave-portal/target/tomcat6x/webapps/ROOT/WEB-INF/classes</destinationFolder>
                         </mapping>
                     </mappings>
                 </configuration>

Added: incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/model/ApplicationData.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/model/ApplicationData.java?rev=1182068&view=auto
==============================================================================
--- incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/model/ApplicationData.java (added)
+++ incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/model/ApplicationData.java Tue Oct 11 20:19:40 2011
@@ -0,0 +1,102 @@
+/*
+ * 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.rave.opensocial.model;
+
+import org.apache.rave.persistence.BasicEntity;
+
+import javax.persistence.*;
+import java.util.Map;
+
+@Entity
+@Table(name = "application_data")
+@NamedQueries(value = {
+        @NamedQuery(name = ApplicationData.FIND_BY_USER_IDS_AND_APP_ID, query = "select a from ApplicationData a " +
+                "where a.userId IN :" + ApplicationData.USER_IDS_PARAM + " AND a.appUrl = :" + ApplicationData.APP_URL_PARAM),
+        @NamedQuery(name = ApplicationData.FIND_BY_USER_ID_AND_APP_ID, query = "select a from ApplicationData a " +
+                "where a.userId = :" + ApplicationData.USER_ID_PARAM + " AND a.appUrl = :" + ApplicationData.APP_URL_PARAM)
+})
+public class ApplicationData implements BasicEntity {
+    public static final String FIND_BY_USER_IDS_AND_APP_ID = "ApplicationData.findByUserIdsAndAppId";
+    public static final String FIND_BY_USER_ID_AND_APP_ID = "ApplicationData.findByUserIdAndAppId";
+
+    public static final String USER_IDS_PARAM = "userIds";
+    public static final String USER_ID_PARAM = "userId";
+    public static final String APP_URL_PARAM = "appUrl";
+
+    /**
+     * The internal object ID used for references to this object.
+     */
+    @Id
+    @GeneratedValue(strategy = GenerationType.TABLE, generator = "applicationDataIdGenerator")
+    @TableGenerator(name = "applicationDataIdGenerator", table = "RAVE_SHINDIG_SEQUENCES", pkColumnName = "SEQ_NAME",
+            valueColumnName = "SEQ_COUNT", pkColumnValue = "application_data", allocationSize = 1, initialValue = 1)
+    @Column(name = "entity_id")
+    private Long entityId;
+
+    @Column(name = "user_id")
+    private String userId;
+
+    @Column(name = "app_url")
+    private String appUrl;
+
+    @Transient
+    private Map<String, String> data;
+
+    public ApplicationData() {
+    }
+
+    public ApplicationData(Long entityId, String userId, String appUrl, Map<String, String> data) {
+        this.entityId = entityId;
+        this.userId = userId;
+        this.appUrl = appUrl;
+        this.data = data;
+    }
+
+    public Long getEntityId() {
+        return entityId;
+    }
+
+    public void setEntityId(Long entityId) {
+        this.entityId = entityId;
+    }
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public String getAppUrl() {
+        return appUrl;
+    }
+
+    public void setAppUrl(String appUrl) {
+        this.appUrl = appUrl;
+    }
+
+    public Map<String, String> getData() {
+        return data;
+    }
+
+    public void setData(Map<String, String> data) {
+        this.data = data;
+    }
+}

Added: incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/repository/ApplicationDataRepository.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/repository/ApplicationDataRepository.java?rev=1182068&view=auto
==============================================================================
--- incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/repository/ApplicationDataRepository.java (added)
+++ incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/repository/ApplicationDataRepository.java Tue Oct 11 20:19:40 2011
@@ -0,0 +1,45 @@
+/*
+ * 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.rave.opensocial.repository;
+
+import org.apache.rave.opensocial.model.ApplicationData;
+import org.apache.rave.persistence.Repository;
+
+import java.util.List;
+
+public interface ApplicationDataRepository extends Repository<ApplicationData> {
+
+    /**
+     * Gets the application data for the given users and application
+     *
+     * @param userIds The users
+     * @param appId   The application
+     * @return The application data for the specified users, or an empty list if none is found
+     */
+    List<ApplicationData> getApplicationData(List<String> userIds, String appId);
+
+    /**
+     * Gets the application data for the given user and application
+     *
+     * @param personId The user
+     * @param appId    The application
+     * @return The application data, or null if not found
+     */
+    ApplicationData getApplicationData(String personId, String appId);
+}

Added: incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/repository/impl/JpaApplicationDataRepository.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/repository/impl/JpaApplicationDataRepository.java?rev=1182068&view=auto
==============================================================================
--- incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/repository/impl/JpaApplicationDataRepository.java (added)
+++ incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/repository/impl/JpaApplicationDataRepository.java Tue Oct 11 20:19:40 2011
@@ -0,0 +1,151 @@
+/*
+ * 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.rave.opensocial.repository.impl;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.rave.exception.DataSerializationException;
+import org.apache.rave.opensocial.model.ApplicationData;
+import org.apache.rave.opensocial.repository.ApplicationDataRepository;
+import org.apache.rave.persistence.jpa.AbstractJpaRepository;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Lob;
+import javax.persistence.TypedQuery;
+import java.util.*;
+
+import static org.apache.rave.persistence.jpa.util.JpaUtil.getSingleResult;
+
+@Repository
+public class JpaApplicationDataRepository extends AbstractJpaRepository<ApplicationData>
+        implements ApplicationDataRepository {
+
+    public JpaApplicationDataRepository() {
+        super(JpaSerializableApplicationData.class);
+    }
+
+    @Override
+    public List<ApplicationData> getApplicationData(List<String> userIds, String appId) {
+        //if the call is only looking for data for a single user use the more efficient single user variant transparently
+        if (userIds.size() == 1) {
+            List<ApplicationData> data = new ArrayList<ApplicationData>();
+            ApplicationData applicationData = getApplicationData(userIds.get(0), appId);
+            if (applicationData != null) {
+                data.add(applicationData);
+            }
+            return data;
+        }
+
+        TypedQuery<JpaSerializableApplicationData> query = manager.createNamedQuery(ApplicationData.FIND_BY_USER_IDS_AND_APP_ID,
+                JpaSerializableApplicationData.class);
+        query.setParameter(ApplicationData.USER_IDS_PARAM, userIds);
+        query.setParameter(ApplicationData.APP_URL_PARAM, appId);
+        List<JpaSerializableApplicationData> results = query.getResultList();
+        for (JpaSerializableApplicationData applicationData : results) {
+            applicationData.deserializeData();
+        }
+        return new ArrayList<ApplicationData>(results);
+    }
+
+    @Override
+    public ApplicationData getApplicationData(String personId, String appId) {
+        TypedQuery<JpaSerializableApplicationData> query = manager.createNamedQuery(ApplicationData.FIND_BY_USER_ID_AND_APP_ID,
+                JpaSerializableApplicationData.class);
+        query.setParameter(ApplicationData.USER_ID_PARAM, personId);
+        query.setParameter(ApplicationData.APP_URL_PARAM, appId);
+        JpaSerializableApplicationData applicationData = getSingleResult(query.getResultList());
+        if (applicationData != null) {
+            applicationData.deserializeData();
+        }
+        return applicationData;
+    }
+
+    @Override
+    public ApplicationData get(long id) {
+        JpaSerializableApplicationData applicationData = (JpaSerializableApplicationData) super.get(id);
+        if (applicationData != null) {
+            applicationData.deserializeData();
+        }
+        return applicationData;
+    }
+
+    @Override
+    @Transactional
+    public ApplicationData save(ApplicationData applicationData) {
+        JpaSerializableApplicationData jpaSerializableApplicationData = getJpaSerializableApplicationData(applicationData);
+        jpaSerializableApplicationData.serializeData();
+        return super.save(jpaSerializableApplicationData);
+    }
+
+    private JpaSerializableApplicationData getJpaSerializableApplicationData(ApplicationData applicationData) {
+        if (applicationData instanceof JpaSerializableApplicationData) {
+            return (JpaSerializableApplicationData) applicationData;
+        }
+
+        return new JpaSerializableApplicationData(applicationData.getEntityId(), applicationData.getUserId(),
+                applicationData.getAppUrl(), applicationData.getData());
+    }
+
+    /**
+     * This class is here so that the details of the persistence strategy in use for serializing the appdata map to a
+     * JSON string doesnt end up being reflected in any public API of the ApplicationData object itself.
+     * <p/>
+     * This allows the public API of this repository to deal in clean ApplicationData models, but under the covers it
+     * uses this model for the actual persistence to the database.
+     */
+    @Entity
+    public static class JpaSerializableApplicationData extends ApplicationData {
+        @Lob
+        @Column(name = "serialized_data")
+        private String serializedData;
+
+        public JpaSerializableApplicationData() {
+            super();
+        }
+
+        public JpaSerializableApplicationData(Long entityId, String userId, String appUrl, Map<String, String> data) {
+            super(entityId, userId, appUrl, data);
+        }
+
+        public void serializeData() {
+            serializedData = new JSONObject(this.getData()).toString();
+        }
+
+        public void deserializeData() {
+            try {
+                Map<String, String> data = new HashMap<String, String>();
+                if (StringUtils.isNotBlank(serializedData)) {
+                    JSONObject jsonObject = new JSONObject(serializedData);
+                    Iterator keys = jsonObject.keys();
+                    while (keys.hasNext()) {
+                        String key = (String) keys.next();
+                        data.put(key, (String) jsonObject.get(key));
+                    }
+                }
+                this.setData(data);
+            } catch (JSONException e) {
+                throw new DataSerializationException("Exception caught while deserializing data: " + serializedData, e);
+            }
+        }
+    }
+}
\ No newline at end of file

Added: incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/service/SimplePersonService.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/service/SimplePersonService.java?rev=1182068&view=auto
==============================================================================
--- incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/service/SimplePersonService.java (added)
+++ incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/service/SimplePersonService.java Tue Oct 11 20:19:40 2011
@@ -0,0 +1,41 @@
+/*
+ * 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.rave.opensocial.service;
+
+import org.apache.rave.opensocial.model.Person;
+import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.social.opensocial.spi.CollectionOptions;
+import org.apache.shindig.social.opensocial.spi.GroupId;
+import org.apache.shindig.social.opensocial.spi.UserId;
+
+import java.util.List;
+import java.util.Set;
+
+public interface SimplePersonService {
+    /**
+     * Returns a list of people that correspond to the passed in person ids.
+     *
+     * @param userIds           A set of users
+     * @param groupId           The group
+     * @param collectionOptions How to filter, sort and paginate the collection being fetched
+     * @param token             The gadget token
+     * @return a list of people
+     */
+    List<Person> getPeople(Set<UserId> userIds, GroupId groupId, CollectionOptions collectionOptions, SecurityToken token);
+}

Modified: incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/service/impl/DefaultAppDataService.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/service/impl/DefaultAppDataService.java?rev=1182068&r1=1182067&r2=1182068&view=diff
==============================================================================
--- incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/service/impl/DefaultAppDataService.java (original)
+++ incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/service/impl/DefaultAppDataService.java Tue Oct 11 20:19:40 2011
@@ -19,37 +19,260 @@
 
 package org.apache.rave.opensocial.service.impl;
 
+import org.apache.commons.lang.StringUtils;
+import org.apache.rave.opensocial.model.ApplicationData;
+import org.apache.rave.opensocial.model.Person;
+import org.apache.rave.opensocial.repository.ApplicationDataRepository;
+import org.apache.rave.opensocial.service.SimplePersonService;
+import org.apache.rave.service.LockService;
 import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.common.util.ImmediateFuture;
 import org.apache.shindig.protocol.DataCollection;
 import org.apache.shindig.protocol.ProtocolException;
 import org.apache.shindig.social.opensocial.spi.AppDataService;
 import org.apache.shindig.social.opensocial.spi.GroupId;
 import org.apache.shindig.social.opensocial.spi.UserId;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import javax.servlet.http.HttpServletResponse;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.concurrent.Future;
+import java.util.concurrent.locks.Lock;
 
 /**
- *
+ * Implementation of the {@link AppDataService} SPI.
  */
 @Service
-public class DefaultAppDataService implements AppDataService{
+public class DefaultAppDataService implements AppDataService {
+    private final SimplePersonService personService;
+    private final LockService lockService;
+    private final ApplicationDataRepository applicationDataRepository;
 
+    /**
+     * These are the only visibility rules I can find in the OpenSocial specification regarding visibility of appdata:
+     * <p/>
+     * "This data store can be read by anyone who can see the gadget, but only the VIEWER's data is writable."
+     * <p/>
+     * So those are the only rules that this implementation currently enforces.
+     */
+
+    @Autowired
+    public DefaultAppDataService(SimplePersonService personService, LockService lockService,
+                                 ApplicationDataRepository applicationDataRepository) {
+        this.personService = personService;
+        this.lockService = lockService;
+        this.applicationDataRepository = applicationDataRepository;
+    }
+
+    /**
+     * Retrieves app data for the specified user list and group.
+     *
+     * @param userIds A set of UserIds
+     * @param groupId The group
+     * @param appId   The application ID
+     * @param fields  The fields to filter the data by - empty set implies no filter
+     * @param token   The security token
+     * @return The data fetched
+     */
     @Override
-    public Future<DataCollection> getPersonData(Set<UserId> userIds, GroupId groupId, String appId, Set<String> fields, SecurityToken token) throws ProtocolException {
-      throw new ProtocolException(HttpServletResponse.SC_NOT_IMPLEMENTED, "Not Implemented");
+    public Future<DataCollection> getPersonData(Set<UserId> userIds, GroupId groupId, String appId, Set<String> fields,
+                                                SecurityToken token) throws ProtocolException {
+        //make sure the request conforms to the OpenSocial visibility rules
+        List<String> personIds = validateReadRequest(userIds, groupId, appId, token);
+
+        //fetch their appdata, convert it to a DataCollection and return it
+        List<ApplicationData> applicationData = applicationDataRepository.getApplicationData(personIds, appId);
+        DataCollection dataCollection = convertAppDataMapToDataCollection(personIds, applicationData, fields);
+        return ImmediateFuture.newInstance(dataCollection);
     }
 
+    /**
+     * Deletes data for the specified user and group.
+     *
+     * @param userId  The user
+     * @param groupId The group
+     * @param appId   The application ID
+     * @param fields  The fields to delete - empty set implies all fields
+     * @param token   The security token
+     * @return an error if one occurs
+     */
     @Override
-    public Future<Void> deletePersonData(UserId userId, GroupId groupId, String appId, Set<String> fields, SecurityToken token) throws ProtocolException {
-      throw new ProtocolException(HttpServletResponse.SC_NOT_IMPLEMENTED, "Not Implemented");
+    public Future<Void> deletePersonData(UserId userId, GroupId groupId, String appId, Set<String> fields,
+                                         SecurityToken token) throws ProtocolException {
+        //make sure the request conforms to the OpenSocial visibility rules
+        String personId = validateWriteRequest(userId, groupId, appId, token);
+
+        //lock on this user and this application to avoid any potential concurrency issues
+        Lock lock = getApplicationDataLock(personId, appId);
+        try {
+            lock.lock();
+
+            //get the application data for this user and application
+            ApplicationData applicationData = applicationDataRepository.getApplicationData(personId, appId);
+
+            //if there is no data, there's nothing to delete, so we're done...
+            if (applicationData == null || applicationData.getData() == null) {
+                return ImmediateFuture.newInstance(null);
+            }
+
+            //remove the fields specified -- empty field set implies remove all, otherwise remove just the fields specified
+            Map<String, String> data = applicationData.getData();
+            if (fields == null || fields.size() == 0) {
+                data.clear();
+            } else {
+                data.keySet().removeAll(fields);
+            }
+
+            //save our changes and return
+            applicationDataRepository.save(applicationData);
+        } finally {
+            lock.unlock();
+            lockService.returnLock(lock);
+        }
+        return ImmediateFuture.newInstance(null);
     }
 
+    /**
+     * Updates app data for the specified user and group with the new values.
+     *
+     * @param userId  The user
+     * @param groupId The group
+     * @param appId   The application ID
+     * @param fields  The fields to update.  Empty set implies that all fields that should be persisted have been
+     *                provided in the values map (completely replace current appData with new data).  A key in the
+     *                fields set without a corresponding key in the values map implies a delete of that field.
+     *                A key in the values map not present in the fields set is a bad request.
+     * @param values  The values to set
+     * @param token   The security token
+     * @return an error if one occurs
+     */
     @Override
-    public Future<Void> updatePersonData(UserId userId, GroupId groupId, String appId, Set<String> fields, Map<String, String> values, SecurityToken token) throws ProtocolException {
-      throw new ProtocolException(HttpServletResponse.SC_NOT_IMPLEMENTED, "Not Implemented");
+    public Future<Void> updatePersonData(UserId userId, GroupId groupId, String appId, Set<String>
+            fields, Map<String, String> values, SecurityToken token) throws ProtocolException {
+        //make sure the request conforms to the OpenSocial visibility rules
+        String personId = validateWriteRequest(userId, groupId, appId, token);
+
+        //lock on this user and this application to avoid any potential concurrency issues
+        Lock lock = getApplicationDataLock(personId, appId);
+        try {
+            lock.lock();
+            //get the application data for this user and application
+            ApplicationData applicationData = applicationDataRepository.getApplicationData(personId, appId);
+
+            //if there is no data, create an empty object to store the data in that we'll save when we're done
+            if (applicationData == null) {
+                applicationData = new ApplicationData(null, personId, appId, new HashMap<String, String>());
+            }
+
+            //if the fields parameter is empty, we can just use the values map directly since this is a full update
+            if (fields == null || fields.size() == 0) {
+                applicationData.setData(values);
+            }
+            //if there are keys in the values map that aren't in the fields set, its a bad request
+            else if (!fields.containsAll(values.keySet())) {
+                throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "Fields parameter must either be empty or contain keys " +
+                        "for all name value pairs sent in request.");
+            }
+            //we have a partial update - we know that the fields set contains keys for all the entries in the values
+            //map (due to the check above), so we can just enumerate over it now to finish our work.  So we want to remove
+            //any fields found in the fields set that are not found in the values map and update the rest.
+            else {
+                Map<String, String> data = applicationData.getData();
+                for (String field : fields) {
+                    //if this field is not in the values map, its a delete
+                    if (!values.containsKey(field)) {
+                        data.remove(field);
+                    } else {
+                        //its an update
+                        data.put(field, values.get(field));
+                    }
+                }
+            }
+
+            //save our changes and return
+            applicationDataRepository.save(applicationData);
+        } finally {
+            lock.unlock();
+            lockService.returnLock(lock);
+        }
+        return ImmediateFuture.newInstance(null);
+    }
+
+    private List<String> validateReadRequest(Set<UserId> userIds, GroupId groupId, String appId, SecurityToken token) {
+        //if the appId in the token matches the appId parameter, then we know the user "can see the gadget"
+        validateAppIdMatches(appId, token);
+
+        //get the people we're supposed to be fetching data for
+        List<Person> people = personService.getPeople(userIds, groupId, null, token);
+        return convertPeopleToUserIds(people);
+    }
+
+    private String validateWriteRequest(UserId userId, GroupId groupId, String appId, SecurityToken token) {
+        //do the read level validation first
+        Set<UserId> userIds = new HashSet<UserId>(Arrays.asList(userId));
+        List<String> personIds = validateReadRequest(userIds, groupId, appId, token);
+
+        //and now check the write level validation which is "only the VIEWER's data is writable"
+        if (personIds.size() != 1 || !personIds.get(0).equalsIgnoreCase(token.getViewerId())) {
+            throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "Writing appdata for anyone but the " +
+                    "current viewer is forbidden.");
+        }
+
+        return personIds.get(0);
+    }
+
+    private void validateAppIdMatches(String appId, SecurityToken token) {
+        if (StringUtils.isBlank(appId) || !appId.equalsIgnoreCase(token.getAppId())) {
+            throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "Requesting appdata for a different " +
+                    "application is forbidden.");
+        }
+    }
+
+    private List<String> convertPeopleToUserIds(List<Person> people) {
+        List<String> ids = new ArrayList<String>(people.size());
+        for (Person person : people) {
+            ids.add(String.valueOf(person.getEntityId()));
+        }
+        return ids;
+    }
+
+    private Lock getApplicationDataLock(String personId, String appId) {
+        return lockService.borrowLock("ApplicationData", new StringBuilder(personId).append("-").append(appId).toString());
+    }
+
+    private DataCollection convertAppDataMapToDataCollection(List<String> personIds, List<ApplicationData> applicationData,
+                                                             Set<String> fields) {
+        //create the map that we'll use to associate users with their appdata
+        Map<String, Map<String, String>> dataCollectionMap = new HashMap<String, Map<String, String>>();
+
+        //enumerate the data we have mapping it back to the owner
+        for (ApplicationData data : applicationData) {
+            //create a map for our return values
+            Map<String, String> returnData = new HashMap<String, String>();
+            //if there isn't a set of fields to filter on return all user data, otherwise filter to the specified fields
+            if (fields == null || fields.size() == 0) {
+                returnData.putAll(data.getData());
+            } else {
+                //otherwise filter the values
+                for (Map.Entry<String, String> userDataEntry : data.getData().entrySet()) {
+                    if (fields.contains(userDataEntry.getKey())) {
+                        returnData.put(userDataEntry.getKey(), userDataEntry.getValue());
+                    }
+                }
+            }
+
+            //put an entry in the data collection mapping the user and their appdata
+            dataCollectionMap.put(data.getUserId(), returnData);
+        }
+
+        //now enumerate all of the personIds to be sure we have some data in the map for them, and if not, add empty data
+        for (String personId : personIds) {
+            if (!dataCollectionMap.containsKey(personId)) {
+                dataCollectionMap.put(personId, new HashMap<String, String>());
+            }
+        }
+
+        return new DataCollection(dataCollectionMap);
     }
-}
+}
\ No newline at end of file

Modified: incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/service/impl/DefaultPersonService.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/service/impl/DefaultPersonService.java?rev=1182068&r1=1182067&r2=1182068&view=diff
==============================================================================
--- incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/service/impl/DefaultPersonService.java (original)
+++ incubator/rave/trunk/rave-shindig/src/main/java/org/apache/rave/opensocial/service/impl/DefaultPersonService.java Tue Oct 11 20:19:40 2011
@@ -21,6 +21,7 @@ package org.apache.rave.opensocial.servi
 
 import com.google.common.collect.Lists;
 import org.apache.rave.opensocial.repository.PersonRepository;
+import org.apache.rave.opensocial.service.SimplePersonService;
 import org.apache.rave.util.CollectionUtils;
 import org.apache.shindig.auth.SecurityToken;
 import org.apache.shindig.common.util.ImmediateFuture;
@@ -44,7 +45,7 @@ import java.util.concurrent.Future;
  * Implementation of the {@link PersonService} SPI
  */
 @Service
-public class DefaultPersonService implements PersonService {
+public class DefaultPersonService implements PersonService, SimplePersonService {
 
     private final PersonRepository repository;
 
@@ -60,7 +61,7 @@ public class DefaultPersonService implem
                                                        Set<String> fields,
                                                        SecurityToken token) throws ProtocolException {
 
-        List<org.apache.rave.opensocial.model.Person> people = getPeopleFromRepository(userIds, groupId, token, collectionOptions);
+        List<org.apache.rave.opensocial.model.Person> people = getPeople(userIds, groupId, collectionOptions, token);
         return ImmediateFuture.newInstance(new RestfulCollection<Person>(convertPeople(people, fields)));
     }
 
@@ -69,10 +70,10 @@ public class DefaultPersonService implem
         return ImmediateFuture.newInstance(convertPerson(getPersonForId(id, token), fields));
     }
 
-    private List<org.apache.rave.opensocial.model.Person> getPeopleFromRepository(Set<UserId> userIds,
-                                                                                  GroupId groupId,
-                                                                                  SecurityToken token,
-                                                                                  CollectionOptions collectionOptions) {
+    @Override
+    public List<org.apache.rave.opensocial.model.Person> getPeople(Set<UserId> userIds, GroupId groupId,
+                                                                   CollectionOptions collectionOptions,
+                                                                   SecurityToken token) {
         switch (groupId.getType()) {
             case all:
                 return getUniqueListOfConnectedPeople(userIds, collectionOptions, token);
@@ -118,13 +119,13 @@ public class DefaultPersonService implem
         String filter = collectionOptions == null ? null : collectionOptions.getFilter();
         List<org.apache.rave.opensocial.model.Person> current;
         //Currently ignoring TOP FRIENDS as it hasn't been defined what a top friend is
-        if(filter == null || filter.equals(PersonService.ALL_FILTER) || filter.equals(PersonService.TOP_FRIENDS_FILTER)) {
+        if (filter == null || filter.equals(PersonService.ALL_FILTER) || filter.equals(PersonService.TOP_FRIENDS_FILTER)) {
             current = repository.findFriends(userId);
 
-        } else if(filter.equals(PersonService.HAS_APP_FILTER)) {
+        } else if (filter.equals(PersonService.HAS_APP_FILTER)) {
             current = repository.findFriends(userId, appId);
 
-        } else if(filter.equals(PersonService.IS_WITH_FRIENDS_FILTER)) {
+        } else if (filter.equals(PersonService.IS_WITH_FRIENDS_FILTER)) {
             current = repository.findFriendsWithFriend(userId, collectionOptions.getFilterValue());
 
         //Represents the default case (filter by field)
@@ -142,13 +143,13 @@ public class DefaultPersonService implem
         List<org.apache.rave.opensocial.model.Person> current;
         //Currently ignoring TOP FRIENDS as it hasn't been defined what a top friend is
 
-        if(filter == null || filter.equals(PersonService.ALL_FILTER) ||filter.equals(PersonService.TOP_FRIENDS_FILTER)) {
+        if (filter == null || filter.equals(PersonService.ALL_FILTER) || filter.equals(PersonService.TOP_FRIENDS_FILTER)) {
             current = repository.findAllConnectedPeople(userId);
 
-        } else if(filter.equals(PersonService.HAS_APP_FILTER)) {
+        } else if (filter.equals(PersonService.HAS_APP_FILTER)) {
             current = repository.findAllConnectedPeople(userId, appId);
 
-        } else if(filter.equals(PersonService.IS_WITH_FRIENDS_FILTER)) {
+        } else if (filter.equals(PersonService.IS_WITH_FRIENDS_FILTER)) {
             current = repository.findAllConnectedPeopleWithFriend(userId, collectionOptions.getFilterValue());
 
         //Represents the default case (filter by field)
@@ -165,13 +166,13 @@ public class DefaultPersonService implem
         String filter = collectionOptions == null ? null : collectionOptions.getFilter();
         List<org.apache.rave.opensocial.model.Person> current;
 
-        if(filter == null || filter.equals(PersonService.ALL_FILTER) || filter.equals(PersonService.TOP_FRIENDS_FILTER)) {
+        if (filter == null || filter.equals(PersonService.ALL_FILTER) || filter.equals(PersonService.TOP_FRIENDS_FILTER)) {
             current = repository.findByGroup(groupId);
 
-        } else if(filter.equals(PersonService.HAS_APP_FILTER)) {
+        } else if (filter.equals(PersonService.HAS_APP_FILTER)) {
             current = repository.findByGroup(groupId, appId);
 
-        } else if(filter.equals(PersonService.IS_WITH_FRIENDS_FILTER)) {
+        } else if (filter.equals(PersonService.IS_WITH_FRIENDS_FILTER)) {
             current = repository.findByGroupWithFriend(groupId, collectionOptions.getFilterValue());
 
         //Represents the default case (filter by field)

Modified: incubator/rave/trunk/rave-shindig/src/main/resources/META-INF/persistence.xml
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-shindig/src/main/resources/META-INF/persistence.xml?rev=1182068&r1=1182067&r2=1182068&view=diff
==============================================================================
--- incubator/rave/trunk/rave-shindig/src/main/resources/META-INF/persistence.xml (original)
+++ incubator/rave/trunk/rave-shindig/src/main/resources/META-INF/persistence.xml Tue Oct 11 20:19:40 2011
@@ -34,6 +34,8 @@
       <class>org.apache.rave.opensocial.model.PersonProperty</class>
       <class>org.apache.rave.opensocial.model.PersonAssociation</class>
       <class>org.apache.rave.opensocial.model.Url</class>
+      <class>org.apache.rave.opensocial.model.ApplicationData</class>
+      <class>org.apache.rave.opensocial.repository.impl.JpaApplicationDataRepository$JpaSerializableApplicationData</class>
       <class>org.apache.rave.gadgets.oauth.model.OAuthTokenInfo</class>
       <class>org.apache.rave.gadgets.oauth.model.OAuthConsumerStore</class>
 

Modified: incubator/rave/trunk/rave-shindig/src/main/resources/initial_data.sql
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-shindig/src/main/resources/initial_data.sql?rev=1182068&r1=1182067&r2=1182068&view=diff
==============================================================================
--- incubator/rave/trunk/rave-shindig/src/main/resources/initial_data.sql (original)
+++ incubator/rave/trunk/rave-shindig/src/main/resources/initial_data.sql Tue Oct 11 20:19:40 2011
@@ -17,6 +17,7 @@
 
 set @token_info_seq = 'token_info';
 set @oauth_consumer_store_seq = 'oauth_consumer_store';
+set @application_data_seq = 'application_data';
 set @person_seq = 'person';
 set @person_association_seq = 'person_association';
 set @groups_seq = 'groups';
@@ -25,6 +26,7 @@ set @group_members_seq = 'group_members'
 CREATE TABLE IF NOT EXISTS RAVE_SHINDIG_SEQUENCES (seq_name VARCHAR(255) PRIMARY KEY NOT NULL, seq_count BIGINT(19));
 INSERT INTO RAVE_SHINDIG_SEQUENCES(seq_name, seq_count) values (@token_info_seq, 1);
 INSERT INTO RAVE_SHINDIG_SEQUENCES(seq_name, seq_count) values (@oauth_consumer_store_seq, 1);
+INSERT INTO RAVE_SHINDIG_SEQUENCES(seq_name, seq_count) values (@application_data_seq, 1);
 INSERT INTO RAVE_SHINDIG_SEQUENCES(seq_name, seq_count) values (@person_seq, 1);
 INSERT INTO RAVE_SHINDIG_SEQUENCES(seq_name, seq_count) values (@person_association_seq, 1);
 INSERT INTO RAVE_SHINDIG_SEQUENCES(seq_name, seq_count) values (@groups_seq, 1);

Modified: incubator/rave/trunk/rave-shindig/src/main/resources/rave-shindig-applicationContext.xml
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-shindig/src/main/resources/rave-shindig-applicationContext.xml?rev=1182068&r1=1182067&r2=1182068&view=diff
==============================================================================
--- incubator/rave/trunk/rave-shindig/src/main/resources/rave-shindig-applicationContext.xml (original)
+++ incubator/rave/trunk/rave-shindig/src/main/resources/rave-shindig-applicationContext.xml Tue Oct 11 20:19:40 2011
@@ -22,9 +22,11 @@
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:p="http://www.springframework.org/schema/p"
+       xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
-        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
+        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
+        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
 
     <!-- make the the rave.shindig.properties props available to autowire injectors, location of the properties can
      be overridden by setting a system property "rave-shindig.override.properties" -->
@@ -37,8 +39,10 @@
     <!-- bean post-processor for JPA annotations -->
     <context:annotation-config/>
 
-    <context:component-scan base-package="org.apache.rave" annotation-config="true"/>
+    <!-- enable the use of the @AspectJ style of Spring AOP -->
+    <aop:aspectj-autoproxy />
 
+    <context:component-scan base-package="org.apache.rave" annotation-config="true"/>
 
     <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
         <property name="entityManagerFactory" ref="entityManagerFactory"/>
@@ -46,7 +50,6 @@
 
     <tx:annotation-driven transaction-manager="transactionManager"/>
 
-
     <bean id="entityManagerFactory"
           class="org.apache.rave.persistence.jpa.PopulatedLocalContainerEntityManagerFactory">
         <property name="persistenceUnitName" value="raveShindigPersistenceUnit"/>
@@ -74,7 +77,6 @@
         <property name="password" value="${rave-shindig.dataSource.password}"/>
     </bean>
 
-
     <bean id="oAuthStore" class="org.apache.rave.gadgets.oauth.inject.DefaultOAuthStore">
         <constructor-arg name="defaultCallbackUrl" value="${shindig.signing.global-callback-url}"/>
         <constructor-arg name="pathToPrivateKey" value="${shindig.signing.key-file}"/>

Added: incubator/rave/trunk/rave-shindig/src/test/java/org/apache/rave/opensocial/repository/JpaApplicationDataRepositoryTest.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-shindig/src/test/java/org/apache/rave/opensocial/repository/JpaApplicationDataRepositoryTest.java?rev=1182068&view=auto
==============================================================================
--- incubator/rave/trunk/rave-shindig/src/test/java/org/apache/rave/opensocial/repository/JpaApplicationDataRepositoryTest.java (added)
+++ incubator/rave/trunk/rave-shindig/src/test/java/org/apache/rave/opensocial/repository/JpaApplicationDataRepositoryTest.java Tue Oct 11 20:19:40 2011
@@ -0,0 +1,151 @@
+/*
+ * 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.rave.opensocial.repository;
+
+import org.apache.rave.opensocial.model.ApplicationData;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.annotation.Rollback;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = {"classpath:rave-shindig-test-applicationContext.xml",
+        "classpath:rave-shindig-test-dataContext.xml"})
+public class JpaApplicationDataRepositoryTest {
+    @PersistenceContext
+    private EntityManager manager;
+
+    @Autowired
+    private ApplicationDataRepository repository;
+
+    private static final String VALID_USER_ID = "12345";
+    private static final String VALID_APPLICATION_ID = "http://example.com/gadget.xml";
+    private static final String SECOND_VALID_APPLICATION_ID = "http://example.com/gadget2.xml";
+    private static final Long VALID_APPLICATION_DATA_ID = 1L;
+
+    private Map<String, String> validApplicationDataMap;
+
+    @Before
+    public void setup() {
+        validApplicationDataMap = new HashMap<String, String>();
+        validApplicationDataMap.put("color", "blue");
+        validApplicationDataMap.put("speed", "fast");
+        validApplicationDataMap.put("state", "MA");
+    }
+
+    @Test
+    public void get_valid() {
+        ApplicationData applicationData = repository.get(VALID_APPLICATION_DATA_ID);
+        validateApplicationData(applicationData);
+    }
+
+    @Test
+    public void get_invalid() {
+        ApplicationData applicationData = repository.get(-1L);
+        assertThat(applicationData, is(nullValue()));
+    }
+
+    @Test
+    public void getApplicationData_byUserIdAndApplicationId_valid() {
+        ApplicationData applicationData = repository.getApplicationData(VALID_USER_ID, VALID_APPLICATION_ID);
+        validateApplicationData(applicationData);
+    }
+
+    @Test
+    public void getApplicationData_byUserIdAndApplicationId_invalid() {
+        ApplicationData applicationData = repository.getApplicationData("-1", VALID_APPLICATION_ID);
+        assertThat(applicationData, is(nullValue()));
+    }
+
+    @Test
+    public void getApplicationData_byUserIdsAndApplicationId_valid() {
+        List<ApplicationData> applicationData = repository.getApplicationData(Arrays.asList(VALID_USER_ID),
+                VALID_APPLICATION_ID);
+        validateApplicationData(applicationData.get(0));
+    }
+
+    @Test
+    public void getApplicationData_byUserIdsAndApplicationId_invalid() {
+        List<ApplicationData> applicationData = repository.getApplicationData(Arrays.asList("-1"),
+                VALID_APPLICATION_ID);
+        assertThat(applicationData, not(nullValue()));
+        assertThat(applicationData.size(), is(equalTo(0)));
+    }
+
+    @Test
+    public void getApplicationData_byUserIdsAndApplicationId_multipleUserIds_valid() {
+        List<ApplicationData> applicationData = repository.getApplicationData(Arrays.asList(VALID_USER_ID, "NO-DATA-USER"),
+                VALID_APPLICATION_ID);
+        //Since there is no appdata in the database for "NO-DATA-USER" we should only get back one result
+        assertThat(applicationData.size(), is(equalTo(1)));
+        validateApplicationData(applicationData.get(0));
+    }
+
+    @Test
+    @Transactional
+    @Rollback(true)
+    public void save_newEntity() {
+        ApplicationData applicationData = new ApplicationData(null, VALID_USER_ID, SECOND_VALID_APPLICATION_ID,
+                validApplicationDataMap);
+
+        ApplicationData saved = repository.save(applicationData);
+        manager.flush();
+        assertThat(saved.getEntityId(), is(notNullValue()));
+    }
+
+    @Test
+    @Transactional
+    @Rollback(true)
+    public void save_existingEntity() {
+        ApplicationData applicationData = new ApplicationData(VALID_APPLICATION_DATA_ID, VALID_USER_ID,
+                VALID_APPLICATION_ID, new HashMap<String, String>());
+
+        ApplicationData saved = repository.save(applicationData);
+        manager.flush();
+        assertThat(saved, is(not(sameInstance(applicationData))));
+        assertThat(saved.getEntityId(), is(equalTo(applicationData.getEntityId())));
+    }
+
+    private void validateApplicationData(ApplicationData applicationData) {
+        assertThat(applicationData, is(not(nullValue())));
+        assertThat(applicationData.getEntityId(), is(equalTo(VALID_APPLICATION_DATA_ID)));
+        assertThat(applicationData.getUserId(), is(equalTo(VALID_USER_ID)));
+        assertThat(applicationData.getAppUrl(), is(equalTo(VALID_APPLICATION_ID)));
+
+        Map<String, String> actualData = applicationData.getData();
+        for (Map.Entry<String, String> entry : validApplicationDataMap.entrySet()) {
+            assertEquals(entry.getValue(), actualData.get(entry.getKey()));
+        }
+    }
+}

Modified: incubator/rave/trunk/rave-shindig/src/test/java/org/apache/rave/opensocial/service/AppDataServiceTest.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-shindig/src/test/java/org/apache/rave/opensocial/service/AppDataServiceTest.java?rev=1182068&r1=1182067&r2=1182068&view=diff
==============================================================================
--- incubator/rave/trunk/rave-shindig/src/test/java/org/apache/rave/opensocial/service/AppDataServiceTest.java (original)
+++ incubator/rave/trunk/rave-shindig/src/test/java/org/apache/rave/opensocial/service/AppDataServiceTest.java Tue Oct 11 20:19:40 2011
@@ -19,8 +19,380 @@
 
 package org.apache.rave.opensocial.service;
 
-/**
- */
+import org.apache.rave.opensocial.model.ApplicationData;
+import org.apache.rave.opensocial.model.Person;
+import org.apache.rave.opensocial.repository.ApplicationDataRepository;
+import org.apache.rave.opensocial.service.impl.DefaultAppDataService;
+import org.apache.rave.service.LockService;
+import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.protocol.DataCollection;
+import org.apache.shindig.protocol.ProtocolException;
+import org.apache.shindig.social.opensocial.spi.AppDataService;
+import org.apache.shindig.social.opensocial.spi.GroupId;
+import org.apache.shindig.social.opensocial.spi.UserId;
+import org.easymock.Capture;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.*;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.locks.ReentrantLock;
+
+import static org.easymock.EasyMock.*;
+import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
 public class AppDataServiceTest {
+    private SimplePersonService personService;
+    private LockService lockService;
+    private ApplicationDataRepository appDataRepository;
+    private AppDataService appDataService;
+
+    private static final String VALID_OWNER_ID = "12345";
+    private static final String VALID_VIEWER_ID = "12345";
+    private static final String VALID_USER_ID = "12345";
+    private static final String VALID_APPLICATION_ID = "http://example.com/gadget.xml";
+    private static final Long VALID_APPLICATION_DATA_ID = 1L;
+    private static final Long VALID_MODULE_ID = 1l;
+
+    private Map<String, String> validApplicationDataMap;
+    private ApplicationData validApplicationData;
+
+    private Person validPerson;
+
+    @Before
+    public void setup() {
+        personService = createMock(SimplePersonService.class);
+        lockService = createMock(LockService.class);
+        appDataRepository = createMock(ApplicationDataRepository.class);
+        appDataService = new DefaultAppDataService(personService, lockService, appDataRepository);
+
+        validApplicationDataMap = new HashMap<String, String>();
+        validApplicationDataMap.put("color", "blue");
+        validApplicationDataMap.put("speed", "fast");
+        validApplicationDataMap.put("state", "MA");
+        validApplicationData = new ApplicationData(VALID_APPLICATION_DATA_ID, VALID_VIEWER_ID, VALID_APPLICATION_ID,
+                validApplicationDataMap);
+
+        validPerson = new Person();
+        validPerson.setEntityId(Long.valueOf(VALID_VIEWER_ID));
+    }
+
+    @Test
+    public void getPersonData_validRequest_hasAppData() throws Exception {
+        testGetPersonData(validApplicationData.getData().keySet(), VALID_OWNER_ID, VALID_VIEWER_ID, VALID_APPLICATION_ID,
+                validApplicationData, validApplicationData.getData());
+    }
+
+    @Test
+    public void getPersonData_validRequest_noAppData() throws Exception {
+        testGetPersonData(validApplicationData.getData().keySet(), VALID_OWNER_ID, VALID_VIEWER_ID, VALID_APPLICATION_ID,
+                null, new HashMap<String, String>());
+    }
+
+    @Test
+    public void getPersonData_validRequest_hasAppData_nullFields() throws Exception {
+        testGetPersonData(null, VALID_OWNER_ID, VALID_VIEWER_ID, VALID_APPLICATION_ID,
+                validApplicationData, validApplicationData.getData());
+    }
+
+    @Test
+    public void getPersonData_validRequest_hasAppData_emptyFields() throws Exception {
+        testGetPersonData(new HashSet<String>(), VALID_OWNER_ID, VALID_VIEWER_ID, VALID_APPLICATION_ID,
+                validApplicationData, validApplicationData.getData());
+    }
+
+    @Test
+    public void getPersonData_validRequest_hasAppData_partialFields() throws Exception {
+        HashMap<String, String> expectedData = new HashMap<String, String>(validApplicationDataMap);
+        expectedData.remove("color");
+
+        testGetPersonData(expectedData.keySet(), VALID_OWNER_ID, VALID_VIEWER_ID, VALID_APPLICATION_ID,
+                validApplicationData, expectedData);
+    }
+
+    @Test(expected = ProtocolException.class)
+    public void getPersonData_invalidRequest_wrongApplication() throws Exception {
+        testGetPersonData(validApplicationData.getData().keySet(), VALID_OWNER_ID, VALID_VIEWER_ID,
+                "http://example.com/wrong.xml", validApplicationData, validApplicationData.getData());
+    }
+
+    @Test
+    public void deletePersonData_validRequest_clearAllDataWithAllFields() throws Exception {
+        Set<String> fieldsToDelete = new HashSet<String>(validApplicationData.getData().keySet());
+
+        testDeletePersonData(fieldsToDelete, new HashMap<String, String>());
+    }
+
+    @Test
+    public void deletePersonData_validRequest_clearAllDataWithNullFields() throws Exception {
+        testDeletePersonData(null, new HashMap<String, String>());
+    }
+
+    @Test
+    public void deletePersonData_validRequest_clearAllDataWithEmptyFields() throws Exception {
+        testDeletePersonData(new HashSet<String>(), new HashMap<String, String>());
+    }
+
+    @Test
+    public void deletePersonData_validRequest_clearSomeData() throws Exception {
+        Set<String> fieldsToDelete = new HashSet<String>(validApplicationData.getData().keySet());
+        fieldsToDelete.remove("color");
+        HashMap<String, String> expectedApplicationDataAfterDelete = new HashMap<String, String>(validApplicationData.getData());
+        for (String fieldToDelete : fieldsToDelete) {
+            expectedApplicationDataAfterDelete.remove(fieldToDelete);
+        }
+
+        testDeletePersonData(fieldsToDelete, expectedApplicationDataAfterDelete);
+    }
+
+    @Test(expected = ProtocolException.class)
+    public void deletePersonData_invalidRequest_wrongViewer() throws Exception {
+        testDeletePersonData("11111", "11111", new HashSet<String>(), new HashMap<String, String>(),
+                validApplicationData);
+    }
+
+    @Test
+    public void deletePersonData_validRequest_nullApplicationData() throws Exception {
+        testDeletePersonDataNoAppDataExpected(null);
+    }
+
+    @Test
+    public void deletePersonData_validRequest_emptyApplicationData() throws Exception {
+        ApplicationData applicationData = new ApplicationData();
+        testDeletePersonDataNoAppDataExpected(applicationData);
+    }
+
+
+    @Test
+    public void updatePersonData_validRequest_removeAllValues() throws Exception {
+        HashMap<String, String> values = new HashMap<String, String>();
+        testUpdatePersonData(null, values, values, validApplicationData);
+    }
+
+    @Test
+    public void updatePersonData_validRequest_replaceAllValues_nullFields() throws Exception {
+        HashMap<String, String> values = new HashMap<String, String>();
+        values.put("newKey1", "newValue1");
+        values.put("newKey2", "newValue2");
+        testUpdatePersonData(null, values, values, validApplicationData);
+    }
+
+    @Test
+    public void updatePersonData_validRequest_replaceAllValues_emptyFields() throws Exception {
+        HashMap<String, String> values = new HashMap<String, String>();
+        values.put("newKey1", "newValue1");
+        values.put("newKey2", "newValue2");
+        testUpdatePersonData(new HashSet<String>(), values, values, validApplicationData);
+    }
+
+    @Test
+    public void updatePersonData_validRequest_appendNewValues() throws Exception {
+        HashMap<String, String> values = new HashMap<String, String>();
+        values.put("newKey1", "newValue1");
+        values.put("newKey2", "newValue2");
+
+        HashMap<String, String> expectedValuesAfterUpdate = new HashMap<String, String>(validApplicationData.getData());
+        expectedValuesAfterUpdate.putAll(values);
+
+        testUpdatePersonData(values.keySet(), values, expectedValuesAfterUpdate, validApplicationData);
+    }
+
+    @Test
+    public void updatePersonData_validRequest_appendNewValues_nullApplicationData() throws Exception {
+        HashMap<String, String> values = new HashMap<String, String>();
+        values.put("newKey1", "newValue1");
+        values.put("newKey2", "newValue2");
+
+        testUpdatePersonData(values.keySet(), values, values, null);
+    }
+
+    @Test
+    public void updatePersonData_validRequest_removeOneField() throws Exception {
+        String propertyToRemove = "color";
+        Set<String> fields = new HashSet<String>();
+        fields.add(propertyToRemove);
+
+        HashMap<String, String> expectedValuesAfterUpdate = new HashMap<String, String>(validApplicationData.getData());
+        expectedValuesAfterUpdate.remove(propertyToRemove);
+
+        testUpdatePersonData(fields, new HashMap<String, String>(), expectedValuesAfterUpdate, validApplicationData);
+    }
+
+    @Test
+    public void updatePersonData_validRequest_updateExistingField() throws Exception {
+        String propertyToUpdate = "color";
+        String updatedValue = "ZZZZZZZZZZ";
+
+        Set<String> fields = new HashSet<String>();
+        fields.add(propertyToUpdate);
+
+        HashMap<String, String> values = new HashMap<String, String>();
+        values.put(propertyToUpdate, updatedValue);
+
+        HashMap<String, String> expectedValuesAfterUpdate = new HashMap<String, String>(validApplicationData.getData());
+        expectedValuesAfterUpdate.put(propertyToUpdate, updatedValue);
+
+        testUpdatePersonData(fields, values, expectedValuesAfterUpdate, validApplicationData);
+    }
+
+    @Test(expected = ProtocolException.class)
+    public void updatePersonData_invalidRequest_invalidArguments() throws Exception {
+        Set<String> fields = new HashSet<String>();
+        fields.add("foo");
+
+        HashMap<String, String> values = new HashMap<String, String>();
+        values.put("a key", "that is not present in the fields set");
+        testUpdatePersonData(fields, values, values, validApplicationData);
+    }
+
+    private void testGetPersonData(Set<String> fields, String ownerId, String viewerId, String applicationId,
+                                   ApplicationData applicationData, Map<String, String> expectedData) throws Exception {
+
+        Set<UserId> userIds = new HashSet<UserId>(Arrays.asList(new UserId(UserId.Type.userId, VALID_USER_ID)));
+
+        SecurityToken securityToken = getMockSecurityToken(ownerId, viewerId, applicationId, VALID_MODULE_ID);
+
+        List<Person> users = Arrays.asList(validPerson);
+        GroupId groupId = new GroupId(GroupId.Type.self, "@self");
+        expect(personService.getPeople(userIds, groupId, null, securityToken)).andReturn(users);
+        replay(personService);
+
+        expect(appDataRepository.getApplicationData(convertPeopleToUserIds(users), applicationId)).andReturn(
+                applicationData == null ? new ArrayList<ApplicationData>() : Arrays.asList(applicationData));
+        replay(appDataRepository);
+
+        Future<DataCollection> result = appDataService.getPersonData(userIds, groupId, VALID_APPLICATION_ID, fields,
+                securityToken);
+        Map<String, String> actualData = result.get().getEntry().get(viewerId);
+        assertThat(actualData, is(not(nullValue())));
+        for (Map.Entry<String, String> entry : expectedData.entrySet()) {
+            assertEquals(entry.getValue(), actualData.get(entry.getKey()));
+        }
+    }
+
+    private void testDeletePersonData(Set<String> fieldsToDelete, HashMap<String, String> expectedApplicationDataAfterDelete) {
+        testDeletePersonData(VALID_OWNER_ID, VALID_VIEWER_ID, fieldsToDelete, expectedApplicationDataAfterDelete,
+                validApplicationData);
+    }
+
+    private void testDeletePersonData(String ownerId, String viewerId, Set<String> fieldsToDelete,
+                                      HashMap<String, String> expectedApplicationDataAfterDelete,
+                                      ApplicationData applicationData) {
+
+        UserId userId = new UserId(UserId.Type.userId, VALID_USER_ID);
+        Set<UserId> userIds = new HashSet<UserId>(Arrays.asList(userId));
+
+        SecurityToken securityToken = getMockSecurityToken(ownerId, viewerId, VALID_APPLICATION_ID, VALID_MODULE_ID);
+
+        List<Person> users = Arrays.asList(validPerson);
+        GroupId groupId = new GroupId(GroupId.Type.self, "@self");
+        expect(personService.getPeople(userIds, groupId, null, securityToken)).andReturn(users);
+        replay(personService);
+
+        expect(appDataRepository.getApplicationData(VALID_USER_ID, VALID_APPLICATION_ID)).andReturn(applicationData);
+        Capture<ApplicationData> capturedApplicationData = new Capture<ApplicationData>();
+        expect(appDataRepository.save(capture(capturedApplicationData))).andReturn(null);
+        replay(appDataRepository);
+
+        ReentrantLock lock = new ReentrantLock();
+        expect(lockService.borrowLock(anyObject(String.class), anyObject(String.class))).andReturn(lock);
+        lockService.returnLock(lock);
+        replay(lockService);
+
+        appDataService.deletePersonData(userId, groupId, VALID_APPLICATION_ID, fieldsToDelete, securityToken);
+
+        ApplicationData expectedApplicationData = new ApplicationData(applicationData.getEntityId(),
+                applicationData.getUserId(), applicationData.getAppUrl(), expectedApplicationDataAfterDelete);
+
+        ApplicationData actualApplicationData = capturedApplicationData.getValue();
+        assertEquals(expectedApplicationData.getEntityId(), actualApplicationData.getEntityId());
+        assertEquals(expectedApplicationData.getUserId(), actualApplicationData.getUserId());
+        assertEquals(expectedApplicationData.getAppUrl(), actualApplicationData.getAppUrl());
+        assertEquals(expectedApplicationData.getData(), actualApplicationData.getData());
+    }
+
+    private void testDeletePersonDataNoAppDataExpected(ApplicationData applicationData) throws InterruptedException, ExecutionException {
+
+        UserId userId = new UserId(UserId.Type.userId, VALID_USER_ID);
+        Set<UserId> userIds = new HashSet<UserId>(Arrays.asList(userId));
+
+        SecurityToken securityToken = getMockSecurityToken(VALID_OWNER_ID, VALID_VIEWER_ID, VALID_APPLICATION_ID, VALID_MODULE_ID);
+
+        List<Person> users = Arrays.asList(validPerson);
+        GroupId groupId = new GroupId(GroupId.Type.self, "@self");
+        expect(personService.getPeople(userIds, groupId, null, securityToken)).andReturn(users);
+        replay(personService);
+
+        expect(appDataRepository.getApplicationData(VALID_USER_ID, VALID_APPLICATION_ID)).andReturn(applicationData);
+        replay(appDataRepository);
+
+        ReentrantLock lock = new ReentrantLock();
+        expect(lockService.borrowLock(anyObject(String.class), anyObject(String.class))).andReturn(lock);
+        lockService.returnLock(lock);
+        replay(lockService);
+
+        Future<Void> result = appDataService.deletePersonData(userId, groupId, VALID_APPLICATION_ID, null, securityToken);
+        assertEquals(null, result.get());
+    }
+
+    private void testUpdatePersonData(Set<String> fields, Map<String, String> values,
+                                      HashMap<String, String> expectedApplicationDataAfterUpdate,
+                                      ApplicationData applicationData) {
+
+        UserId userId = new UserId(UserId.Type.userId, VALID_USER_ID);
+        Set<UserId> userIds = new HashSet<UserId>(Arrays.asList(userId));
+
+        SecurityToken securityToken = getMockSecurityToken(VALID_OWNER_ID, VALID_VIEWER_ID, VALID_APPLICATION_ID, VALID_MODULE_ID);
+
+        List<Person> users = Arrays.asList(validPerson);
+        GroupId groupId = new GroupId(GroupId.Type.self, "@self");
+        expect(personService.getPeople(userIds, groupId, null, securityToken)).andReturn(users);
+        replay(personService);
+
+        expect(appDataRepository.getApplicationData(VALID_USER_ID, VALID_APPLICATION_ID)).andReturn(applicationData);
+        Capture<ApplicationData> capturedApplicationData = new Capture<ApplicationData>();
+        expect(appDataRepository.save(capture(capturedApplicationData))).andReturn(null);
+        replay(appDataRepository);
+
+        ReentrantLock lock = new ReentrantLock();
+        expect(lockService.borrowLock(anyObject(String.class), anyObject(String.class))).andReturn(lock);
+        lockService.returnLock(lock);
+        replay(lockService);
+
+        appDataService.updatePersonData(userId, groupId, VALID_APPLICATION_ID, fields, values, securityToken);
+
+        ApplicationData expectedApplicationData = applicationData == null ? new ApplicationData(null, VALID_USER_ID,
+                VALID_APPLICATION_ID, expectedApplicationDataAfterUpdate) :
+                new ApplicationData(applicationData.getEntityId(), applicationData.getUserId(),
+                        applicationData.getAppUrl(), expectedApplicationDataAfterUpdate);
+
+        ApplicationData actualApplicationData = capturedApplicationData.getValue();
+        assertEquals(expectedApplicationData.getEntityId(), actualApplicationData.getEntityId());
+        assertEquals(expectedApplicationData.getUserId(), actualApplicationData.getUserId());
+        assertEquals(expectedApplicationData.getAppUrl(), actualApplicationData.getAppUrl());
+        assertEquals(expectedApplicationData.getData(), actualApplicationData.getData());
+    }
+
+    private SecurityToken getMockSecurityToken(String ownerId, String viewerId, String applicationId, Long moduleID) {
+        SecurityToken securityToken;
+        securityToken = createNiceMock(SecurityToken.class);
+        expect(securityToken.getOwnerId()).andReturn(ownerId);
+        expect(securityToken.getViewerId()).andReturn(viewerId);
+        expect(securityToken.getAppId()).andReturn(applicationId);
+        expect(securityToken.getModuleId()).andReturn(moduleID);
+        replay(securityToken);
+        return securityToken;
+    }
 
+    private List<String> convertPeopleToUserIds(List<Person> people) {
+        List<String> ids = new ArrayList<String>(people.size());
+        for (Person person : people) {
+            ids.add(String.valueOf(person.getEntityId()));
+        }
+        return ids;
+    }
 }

Modified: incubator/rave/trunk/rave-shindig/src/test/resources/test_data.sql
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-shindig/src/test/resources/test_data.sql?rev=1182068&r1=1182067&r2=1182068&view=diff
==============================================================================
--- incubator/rave/trunk/rave-shindig/src/test/resources/test_data.sql (original)
+++ incubator/rave/trunk/rave-shindig/src/test/resources/test_data.sql Tue Oct 11 20:19:40 2011
@@ -16,6 +16,7 @@
  -- under the License.
 set @token_info_seq = 'token_info';
 set @oauth_consumer_store_seq = 'oauth_consumer_store';
+set @application_data_seq = 'application_data';
 
 set @token_info_id_1 = (SELECT seq_count FROM RAVE_SHINDIG_SEQUENCES WHERE seq_name = @token_info_seq);
 INSERT INTO oauth_token_info(entity_id, access_token, token_secret, session_handle, token_expire_millis, app_url, module_id, token_name, service_name, user_id)
@@ -26,3 +27,8 @@ set @consumer_store_id_1 = (SELECT seq_c
 INSERT INTO oauth_consumer_store(entity_id, gadget_uri, service_name, consumer_key, consumer_secret, key_type, key_name, callback_url)
 VALUES (@consumer_store_id_1, 'http://localhost:8080/samplecontainer/examples/oauth.xml', 'Google', 'gadgetConsumer', 'gadgetSecret', 'HMAC_SYMMETRIC', 'keyName', 'http://oauth.gmodules.com/gadgets/oauthcallback');
 UPDATE RAVE_SHINDIG_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @oauth_consumer_store_seq;
+
+set @application_data_id_1 = (SELECT seq_count FROM RAVE_SHINDIG_SEQUENCES WHERE seq_name = @application_data_seq);
+INSERT INTO application_data(entity_id, user_id, app_url, serialized_data, dtype)
+VALUES (@application_data_id_1, '12345', 'http://example.com/gadget.xml', '{"color":"blue","speed":"fast","state":"MA"}', 'JpaApplicationDataRepository$JpaSerializableApplicationData');
+UPDATE RAVE_SHINDIG_SEQUENCES SET seq_count = (seq_count + 1) WHERE seq_name = @application_data_seq;
\ No newline at end of file