You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by li...@apache.org on 2010/08/27 14:00:53 UTC
svn commit: r990115 - in /shindig/trunk: content/sampledata/
java/social-api/src/main/java/org/apache/shindig/social/core/config/
java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/
java/social-api/src/main/java/org/apache/shind...
Author: lindner
Date: Fri Aug 27 12:00:52 2010
New Revision: 990115
URL: http://svn.apache.org/viewvc?rev=990115&view=rev
Log:
SHINDIG-1410 | Patch from Eric Woods | Album/MediaItem implementation
Added:
shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/AlbumHandler.java
shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/MediaItemHandler.java
shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/AlbumService.java
shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/MediaItemService.java
Modified:
shindig/trunk/content/sampledata/canonicaldb.json
shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/config/SocialApiGuiceModule.java
shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/SampleModule.java
shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialService.java
Modified: shindig/trunk/content/sampledata/canonicaldb.json
URL: http://svn.apache.org/viewvc/shindig/trunk/content/sampledata/canonicaldb.json?rev=990115&r1=990114&r2=990115&view=diff
==============================================================================
--- shindig/trunk/content/sampledata/canonicaldb.json (original)
+++ shindig/trunk/content/sampledata/canonicaldb.json Fri Aug 27 12:00:52 2010
@@ -369,6 +369,25 @@
}
}]
},
+"albums" : {
+ "john.doe": [{
+ "id" : "album123",
+ "ownerId" : "john.doe",
+ "thumbnailUrl" : "http://pages.example.org/albums/4433221-tn.png",
+ "title" : "Example Album",
+ "description" : "This is an example album, and this text is an example description",
+ "location" : { "latitude": 0, "longitude": 0 }
+ }]
+},
+"mediaItems" : {
+ "john.doe": [{
+ "id" : "mediaItem123",
+ "albumId" : "album123",
+ "mimeType" : "image/jpeg",
+ "type" : "image",
+ "url" : "http://animals.nationalgeographic.com/staticfiles/NGS/Shared/StaticFiles/animals/images/primary/black-spider-monkey.jpg"
+ }]
+},
//
// ----------------------------- Data ---------------------------------------
//
Modified: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/config/SocialApiGuiceModule.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/config/SocialApiGuiceModule.java?rev=990115&r1=990114&r2=990115&view=diff
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/config/SocialApiGuiceModule.java (original)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/config/SocialApiGuiceModule.java Fri Aug 27 12:00:52 2010
@@ -18,6 +18,9 @@
package org.apache.shindig.social.core.config;
+import java.util.List;
+import java.util.Set;
+
import org.apache.shindig.auth.AnonymousAuthenticationHandler;
import org.apache.shindig.auth.AuthenticationHandler;
import org.apache.shindig.common.servlet.ParameterFetcher;
@@ -30,13 +33,12 @@ import org.apache.shindig.social.core.oa
import org.apache.shindig.social.core.util.BeanXStreamAtomConverter;
import org.apache.shindig.social.core.util.xstream.XStream081Configuration;
import org.apache.shindig.social.opensocial.service.ActivityHandler;
+import org.apache.shindig.social.opensocial.service.AlbumHandler;
import org.apache.shindig.social.opensocial.service.AppDataHandler;
+import org.apache.shindig.social.opensocial.service.MediaItemHandler;
import org.apache.shindig.social.opensocial.service.MessageHandler;
import org.apache.shindig.social.opensocial.service.PersonHandler;
-import java.util.List;
-import java.util.Set;
-
import com.google.common.collect.ImmutableSet;
import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;
@@ -83,6 +85,6 @@ public class SocialApiGuiceModule extend
*/
protected Set<Class<?>> getHandlers() {
return ImmutableSet.<Class<?>>of(ActivityHandler.class, AppDataHandler.class,
- PersonHandler.class, MessageHandler.class);
+ PersonHandler.class, MessageHandler.class, AlbumHandler.class, MediaItemHandler.class);
}
}
Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/AlbumHandler.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/AlbumHandler.java?rev=990115&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/AlbumHandler.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/AlbumHandler.java Fri Aug 27 12:00:52 2010
@@ -0,0 +1,185 @@
+/*
+ * 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.service;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Future;
+
+import org.apache.shindig.config.ContainerConfig;
+import org.apache.shindig.protocol.HandlerPreconditions;
+import org.apache.shindig.protocol.Operation;
+import org.apache.shindig.protocol.ProtocolException;
+import org.apache.shindig.protocol.RequestItem;
+import org.apache.shindig.protocol.Service;
+import org.apache.shindig.social.opensocial.model.Album;
+import org.apache.shindig.social.opensocial.spi.AlbumService;
+import org.apache.shindig.social.opensocial.spi.CollectionOptions;
+import org.apache.shindig.social.opensocial.spi.UserId;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.inject.Inject;
+
+/*
+ * Receives and delegates requests to the OpenSocial Album service.
+ *
+ * TODO: test cases
+ */
+@Service(name = "albums", path = "/{userId}+/{groupId}/{albumId}+")
+public class AlbumHandler {
+
+ private final AlbumService service;
+ private final ContainerConfig config;
+
+ @Inject
+ public AlbumHandler(AlbumService service, ContainerConfig config) {
+ this.service = service;
+ this.config = config;
+ }
+
+ /*
+ * Handles create operations.
+ *
+ * Allowed end-points: /albums/{userId}/@self
+ *
+ * Examples: /albums/john.doe/@self
+ */
+ @Operation(httpMethods = "POST", bodyParam = "album")
+ public Future<?> create(SocialRequestItem request) throws ProtocolException {
+ // Retrieve userIds and albumIds
+ Set<UserId> userIds = request.getUsers();
+ List<String> albumIds = request.getListParameter("albumId");
+
+ // Preconditions - exactly one userId specified, no albumIds specified
+ HandlerPreconditions.requireNotEmpty(userIds, "No userId specified");
+ HandlerPreconditions.requireSingular(userIds, "Multiple userIds not supported");
+ HandlerPreconditions.requireEmpty(albumIds, "Cannot specify albumId in create");
+
+ return service.createAlbum(Iterables.getOnlyElement(userIds),
+ request.getAppId(),
+ request.getTypedParameter("album", Album.class),
+ request.getToken());
+ }
+
+ /*
+ * Handles retrieve operations.
+ *
+ * Allowed end-points: /albums/{userId}+/{groupId}/{albumId}+
+ *
+ * Examples: /albums/@me/@self /albums/john.doe/@self/1,2
+ * /albums/john.doe,jane.doe/@friends
+ */
+ @Operation(httpMethods = "GET")
+ public Future<?> get(SocialRequestItem request) throws ProtocolException {
+ // Get user, group, and album IDs
+ Set<UserId> userIds = request.getUsers();
+ Set<String> optionalAlbumIds = ImmutableSet.copyOf(request
+ .getListParameter("albumId"));
+
+ // At least one userId must be specified
+ HandlerPreconditions.requireNotEmpty(userIds, "No userId specified");
+
+ // If multiple userIds specified, albumIds must not be specified
+ if (userIds.size() > 1 && !optionalAlbumIds.isEmpty()) {
+ throw new IllegalArgumentException("Cannot fetch same albumIds for multiple userIds");
+ }
+
+ // Retrieve albums by ID
+ if (!optionalAlbumIds.isEmpty()) {
+ if (optionalAlbumIds.size() == 1) {
+ return service.getAlbum(Iterables.getOnlyElement(userIds),
+ request.getAppId(), request.getFields(),
+ optionalAlbumIds.iterator().next(), request.getToken());
+ } else {
+ return service.getAlbums(Iterables.getOnlyElement(userIds),
+ request.getAppId(), request.getFields(),
+ new CollectionOptions(request), optionalAlbumIds,
+ request.getToken());
+ }
+ }
+
+ // Retrieve albums by group
+ return service.getAlbums(userIds, request.getGroup(), request
+ .getAppId(), request.getFields(),
+ new CollectionOptions(request), request.getToken());
+ }
+
+ /*
+ * Handles update operations.
+ *
+ * Allowed end-points: /albums/{userId}/@self/{albumId}
+ *
+ * Examples: /albums/john.doe/@self/1
+ */
+ @Operation(httpMethods = "PUT", bodyParam = "album")
+ public Future<?> update(SocialRequestItem request) throws ProtocolException {
+ // Retrieve userIds and albumIds
+ Set<UserId> userIds = request.getUsers();
+ List<String> albumIds = request.getListParameter("albumId");
+
+ // Enforce preconditions - exactly one user and one album specified
+ HandlerPreconditions.requireNotEmpty(userIds, "No userId specified");
+ HandlerPreconditions.requireSingular(userIds, "Multiple userIds not supported");
+ HandlerPreconditions.requireNotEmpty(albumIds, "No albumId specified");
+ HandlerPreconditions.requireSingular(albumIds, "Multiple albumIds not supported");
+
+ return service.updateAlbum(Iterables.getOnlyElement(userIds),
+ request.getAppId(),
+ request.getTypedParameter("album", Album.class),
+ Iterables.getOnlyElement(albumIds), request.getToken());
+ }
+
+ /*
+ * Handles delete operations.
+ *
+ * Allowed end-points: /albums/{userId}/@self/{albumId}
+ *
+ * Examples: /albums/john.doe/@self/1
+ */
+ @Operation(httpMethods = "DELETE")
+ public Future<?> delete(SocialRequestItem request) throws ProtocolException {
+ // Get user and album ID
+ Set<UserId> userIds = request.getUsers();
+ String albumId = request.getParameter("albumId");
+
+ // Enforce preconditions - userIds must contain exactly one element
+ HandlerPreconditions.requireNotEmpty(userIds, "No userId specified");
+ HandlerPreconditions.requireSingular(userIds, "Multiple userIds not supported");
+
+ // Service request
+ return service.deleteAlbum(Iterables.getOnlyElement(userIds),
+ request.getAppId(), albumId, request.getToken());
+ }
+
+ /*
+ * Retrieves supported fields for the albums service.
+ */
+ @Operation(httpMethods = "GET", path = "/@supportedFields")
+ public List<Object> supportedFields(RequestItem request) {
+ String container = firstNonNull(request.getToken().getContainer(),
+ ContainerConfig.DEFAULT_CONTAINER);
+ return config.getList(container,
+ "${Cur['gadgets.features'].opensocial.supportedFields.album}");
+ }
+
+ private static <T> T firstNonNull(T first, T second) {
+ return first != null ? first : Preconditions.checkNotNull(second);
+ }
+}
\ No newline at end of file
Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/MediaItemHandler.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/MediaItemHandler.java?rev=990115&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/MediaItemHandler.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/MediaItemHandler.java Fri Aug 27 12:00:52 2010
@@ -0,0 +1,202 @@
+package org.apache.shindig.social.opensocial.service;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Future;
+
+import org.apache.shindig.config.ContainerConfig;
+import org.apache.shindig.protocol.HandlerPreconditions;
+import org.apache.shindig.protocol.Operation;
+import org.apache.shindig.protocol.ProtocolException;
+import org.apache.shindig.protocol.RequestItem;
+import org.apache.shindig.protocol.Service;
+import org.apache.shindig.social.opensocial.model.MediaItem;
+import org.apache.shindig.social.opensocial.spi.CollectionOptions;
+import org.apache.shindig.social.opensocial.spi.MediaItemService;
+import org.apache.shindig.social.opensocial.spi.UserId;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.inject.Inject;
+
+/*
+ * Receives and delegates requests to the OpenSocial MediaItems service.
+ *
+ * TODO: test cases
+ */
+@Service(name = "mediaItems", path = "/{userId}+/{groupId}/{albumId}/{mediaItemId}+")
+public class MediaItemHandler {
+
+
+ private final MediaItemService service;
+ private final ContainerConfig config;
+
+ @Inject
+ public MediaItemHandler(MediaItemService service, ContainerConfig config) {
+ this.service = service;
+ this.config = config;
+ }
+
+ /*
+ * Handles GET operations.
+ *
+ * Allowed end-points: /mediaItems/{userId}+/{groupId}/{albumId}/{mediaItemId}+
+ *
+ * Examples: /mediaItems/john.doe/@self
+ * /mediaItems/john.doe,jane.doe/@self
+ * /mediaItems/john.doe/@self/album123
+ * /mediaItems/john.doe/@self/album123/1,2,3
+ */
+ @Operation(httpMethods = "GET")
+ public Future<?> get(SocialRequestItem request) throws ProtocolException {
+ // Get user, group, album IDs, and MediaItem IDs
+ Set<UserId> userIds = request.getUsers();
+ Set<String> optionalAlbumIds = ImmutableSet.copyOf(request.getListParameter("albumId"));
+ Set<String> optionalMediaItemIds = ImmutableSet.copyOf(request.getListParameter("mediaItemId"));
+
+ // At least one userId must be specified
+ HandlerPreconditions.requireNotEmpty(userIds, "No user ID specified");
+
+ // Get Album ID; null if not provided
+ String albumId = null;
+ if (optionalAlbumIds.size() == 1) {
+ albumId = Iterables.getOnlyElement(optionalAlbumIds);
+ } else if (optionalAlbumIds.size() > 1) {
+ throw new IllegalArgumentException("Multiple Album IDs not supported");
+ }
+
+ // Cannot retrieve by ID if album ID not provided
+ if (albumId == null && !optionalMediaItemIds.isEmpty()) {
+ throw new IllegalArgumentException("Cannot fetch by MediaItem ID without Album ID");
+ }
+
+ // Cannot retrieve by ID or album if multiple user's given
+ if (userIds.size() > 1) {
+ if (!optionalMediaItemIds.isEmpty()) {
+ throw new IllegalArgumentException("Cannot fetch MediaItem by ID for multiple users");
+ } else if (albumId != null) {
+ throw new IllegalArgumentException("Cannot fetch MediaItem by Album for multiple users");
+ }
+ }
+
+ // Retrieve by ID(s)
+ if (!optionalMediaItemIds.isEmpty()) {
+ if (optionalMediaItemIds.size() == 1) {
+ return service.getMediaItem(Iterables.getOnlyElement(userIds),
+ request.getAppId(), albumId,
+ Iterables.getOnlyElement(optionalMediaItemIds),
+ request.getFields(), request.getToken());
+ } else {
+ return service.getMediaItems(Iterables.getOnlyElement(userIds),
+ request.getAppId(), albumId, optionalMediaItemIds,
+ request.getFields(), new CollectionOptions(request),
+ request.getToken());
+ }
+ }
+
+ // Retrieve by Album
+ if (albumId != null) {
+ return service.getMediaItems(Iterables.getOnlyElement(userIds),
+ request.getAppId(), albumId, request.getFields(),
+ new CollectionOptions(request), request.getToken());
+ }
+
+ // Retrieve by users and groups
+ return service.getMediaItems(userIds, request.getGroup(), request
+ .getAppId(), request.getFields(),
+ new CollectionOptions(request), request.getToken());
+ }
+
+ /*
+ * Handles DELETE operations.
+ *
+ * Allowed end-points: /mediaItem/{userId}/@self/{albumId}/{mediaItemId}
+ *
+ * Examples: /mediaItems/john.doe/@self/1/2
+ */
+ @Operation(httpMethods = "DELETE")
+ public Future<?> delete(SocialRequestItem request) throws ProtocolException {
+ // Get users, Album ID, and MediaItem ID
+ Set<UserId> userIds = request.getUsers();
+ Set<String> albumIds = ImmutableSet.copyOf(request.getListParameter("albumId"));
+ Set<String> mediaItemIds = ImmutableSet.copyOf(request.getListParameter("mediaItemId"));
+
+ // Exactly one user, Album, and MediaItem must be specified
+ HandlerPreconditions.requireNotEmpty(userIds, "No userId specified");
+ HandlerPreconditions.requireSingular(userIds, "Exactly one user ID must be specified");
+ HandlerPreconditions.requireSingular(albumIds, "Exactly one Album ID must be specified");
+ HandlerPreconditions.requireSingular(mediaItemIds, "Exactly one MediaItem ID must be specified");
+
+ // Service request
+ return service.deleteMediaItem(Iterables.getOnlyElement(userIds),
+ request.getAppId(), Iterables.getOnlyElement(albumIds),
+ Iterables.getOnlyElement(mediaItemIds), request.getToken());
+ }
+
+ /*
+ * Handles POST operations.
+ *
+ * Allowed end-points: /mediaItems/{userId}/@self/{albumId}
+ *
+ * Examples: /mediaItems/john.doe/@self/1
+ */
+ @Operation(httpMethods = "POST", bodyParam = "mediaItem")
+ public Future<?> create(SocialRequestItem request) throws ProtocolException {
+ // Retrieve userIds and albumIds
+ Set<UserId> userIds = request.getUsers();
+ Set<String> albumIds = ImmutableSet.copyOf(request.getListParameter("albumId"));
+
+ // Exactly one user and Album must be specified
+ HandlerPreconditions.requireNotEmpty(userIds, "No userId specified");
+ HandlerPreconditions.requireSingular(userIds, "Exactly one user ID must be specified");
+ HandlerPreconditions.requireSingular(albumIds, "Exactly one Album ID must be specified");
+
+ // Service request
+ return service.createMediaItem(Iterables.getOnlyElement(userIds),
+ request.getAppId(), Iterables.getOnlyElement(albumIds),
+ request.getTypedParameter("mediaItem", MediaItem.class),
+ request.getToken());
+ }
+
+ /*
+ * Handles PUT operations.
+ *
+ * Allowed end-points: /mediaItems/{userId}/@self/{albumId}/{mediaItemId}
+ *
+ * Examples: /mediaItems/john.doe/@self/1/2
+ */
+ @Operation(httpMethods = "PUT", bodyParam = "mediaItem")
+ public Future<?> update(SocialRequestItem request) throws ProtocolException {
+ // Retrieve userIds, albumIds, and mediaItemIds
+ Set<UserId> userIds = request.getUsers();
+ Set<String> albumIds = ImmutableSet.copyOf(request.getListParameter("albumId"));
+ Set<String> mediaItemIds = ImmutableSet.copyOf(request.getListParameter("mediaItemIds"));
+
+ // Exactly one user, Album, and MediaItem must be specified
+ HandlerPreconditions.requireNotEmpty(userIds, "No userId specified");
+ HandlerPreconditions.requireSingular(userIds, "Exactly one user ID must be specified");
+ HandlerPreconditions.requireSingular(albumIds, "Exactly one Album ID must be specified");
+ HandlerPreconditions.requireSingular(mediaItemIds, "Exactly one MediaItem ID must be specified");
+
+ // Service request
+ return service.updateMediaItem(Iterables.getOnlyElement(userIds),
+ request.getAppId(), Iterables.getOnlyElement(albumIds),
+ Iterables.getOnlyElement(mediaItemIds),
+ request.getTypedParameter("mediaItem", MediaItem.class),
+ request.getToken());
+ }
+
+ @Operation(httpMethods = "GET", path = "/@supportedFields")
+ public List<Object> supportedFields(RequestItem request) {
+ // TODO: Would be nice if name in config matched name of service.
+ String container = firstNonNull(request.getToken().getContainer(),
+ ContainerConfig.DEFAULT_CONTAINER);
+ return config.getList(container,
+ "${Cur['gadgets.features'].opensocial.supportedFields.mediaItem}");
+ }
+
+ private static <T> T firstNonNull(T first, T second) {
+ return first != null ? first : Preconditions.checkNotNull(second);
+ }
+}
Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/AlbumService.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/AlbumService.java?rev=990115&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/AlbumService.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/AlbumService.java Fri Aug 27 12:00:52 2010
@@ -0,0 +1,121 @@
+/*
+ * 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.spi;
+
+import java.util.Set;
+import java.util.concurrent.Future;
+
+import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.protocol.ProtocolException;
+import org.apache.shindig.protocol.RestfulCollection;
+import org.apache.shindig.social.opensocial.model.Album;
+
+/*
+ * The AlbumService interface defines the service provider interface for
+ * creating, retrieving, updating, and deleting OpenSocial albums.
+ */
+public interface AlbumService {
+
+ /*
+ * Retrieves a single album for the given user with the given album ID.
+ *
+ * @param userId Identifies the person to retrieve the album from
+ * @param appId Identifies the application to retrieve the album from
+ * @param fields Indicates the fields to return. Empty set implies all
+ * @param albumId Identifies the album to retrieve
+ * @param token A valid SecurityToken
+ *
+ * @return a response item with the requested album
+ */
+ Future<Album> getAlbum(UserId userId, String appId, Set<String> fields,
+ String albumId, SecurityToken token) throws ProtocolException;
+
+ /*
+ * Retrieves albums for the given user with the given album IDs.
+ *
+ * @param userId Identifies the person to retrieve albums for
+ * @param appId Identifies the application to retrieve albums from
+ * @param fields The fields to return; empty set implies all
+ * @param options The sorting/filtering/pagination options
+ * @param albumIds The set of album ids to fetch
+ * @param token A valid SecurityToken
+ *
+ * @return a response item with requested albums
+ */
+ Future<RestfulCollection<Album>> getAlbums(UserId userId, String appId,
+ Set<String> fields, CollectionOptions options,
+ Set<String> albumIds, SecurityToken token) throws ProtocolException;
+
+ /*
+ * Retrieves albums for the given user and group.
+ *
+ * @param userIds Identifies the users to retrieve albums from
+ * @param groupId Identifies the group to retrieve albums from
+ * @param appId Identifies the application to retrieve albums from
+ * @param fields The fields to return. Empty set implies all
+ * @param options The sorting/filtering/pagination options
+ * @param token A valid SecurityToken
+ *
+ * @return a response item with the requested albums
+ */
+ Future<RestfulCollection<Album>> getAlbums(Set<UserId> userIds,
+ GroupId groupId, String appId, Set<String> fields,
+ CollectionOptions options, SecurityToken token)
+ throws ProtocolException;
+
+ /*
+ * Deletes a single album for the given user with the given album ID.
+ *
+ * @param userId Identifies the user to delete the album from
+ * @param appId Identifies the application to delete the album from
+ * @param albumId Identifies the album to delete
+ * @param token A valid SecurityToken
+ *
+ * @return a response item containing any errors
+ */
+ Future<Void> deleteAlbum(UserId userId, String appId, String albumId,
+ SecurityToken token) throws ProtocolException;
+
+ /*
+ * Creates an album for the given user.
+ *
+ * @param userId Identifies the user to create the album for
+ * @param appId Identifies the application to create the album in
+ * @param album The album to create
+ * @param token A valid SecurityToken
+ *
+ * @return a response containing any errors
+ */
+ Future<Void> createAlbum(UserId userId, String appId, Album album,
+ SecurityToken token) throws ProtocolException;
+
+ /*
+ * Updates an album for the given user. The album ID specified in the REST
+ * end-point is used, even if the album also defines an ID.
+ *
+ * @param userId Identifies the user to update the album for
+ * @param appId Identifies the application to update the album in
+ * @param album Defines the updated album
+ * @param albumId Identifies the ID of the album to update
+ * @param token A valid SecurityToken
+ *
+ * @return a response containing any errors
+ */
+ Future<Void> updateAlbum(UserId userId, String appId, Album album,
+ String albumId, SecurityToken token) throws ProtocolException;
+}
\ No newline at end of file
Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/MediaItemService.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/MediaItemService.java?rev=990115&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/MediaItemService.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/MediaItemService.java Fri Aug 27 12:00:52 2010
@@ -0,0 +1,129 @@
+package org.apache.shindig.social.opensocial.spi;
+
+import java.util.Set;
+import java.util.concurrent.Future;
+
+import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.protocol.ProtocolException;
+import org.apache.shindig.protocol.RestfulCollection;
+import org.apache.shindig.social.opensocial.model.MediaItem;
+
+/*
+ * The MediaItemService interface defines the service provider interface for
+ * creating, retrieving, updating, and deleting OpenSocial MediaItems.
+ */
+public interface MediaItemService {
+
+ /*
+ * Retrieves a MediaItem by ID.
+ *
+ * @param userId Identifies the owner of the MediaItem to retrieve
+ * @param appId Identifies the application of the MeiaItem to retrieve
+ * @param albumId Identifies the album containing the MediaItem
+ * @param mediaItemId Identifies the MediaItem to retrieve
+ * @param fields Indicates fields to be returned; empty set implies all
+ * @param token A valid SecurityToken
+ *
+ * @return a response item with the requested MediaItem
+ */
+ Future<MediaItem> getMediaItem(UserId userId, String appId, String albumId,
+ String mediaItemId, Set<String> fields, SecurityToken token)
+ throws ProtocolException;
+
+ /*
+ * Retrieves MediaItems by IDs.
+ *
+ * @param userId Identifies the owner of the MediaItems
+ * @param appId Identifies the application of the MediaItems
+ * @param albumId Identifies the album containing the MediaItems
+ * @param mediaItemIds Identifies the MediaItems to retrieve
+ * @param fields Specifies the fields to return; empty set implies all
+ * @param options Sorting/filtering/pagination options
+ * @param token A valid SecurityToken
+ *
+ * @return a response item with the requested MediaItems
+ */
+ Future<RestfulCollection<MediaItem>> getMediaItems(UserId userId,
+ String appId, String albumId, Set<String> mediaItemIds,
+ Set<String> fields, CollectionOptions options, SecurityToken token)
+ throws ProtocolException;
+
+ /*
+ * Retrieves MediaItems by Album.
+ *
+ * @param userId Identifies the owner of the MediaItems
+ * @param appId Identifies the application of the MediaItems
+ * @param albumId Identifies the Album containing the MediaItems
+ * @param fields Specifies the fields to return; empty set implies all
+ * @param options Sorting/filtering/pagination options
+ * @param token A valid SecurityToken
+ *
+ * @return a response item with the requested MediaItems
+ */
+ Future<RestfulCollection<MediaItem>> getMediaItems(UserId userId,
+ String appId, String albumId, Set<String> fields,
+ CollectionOptions options, SecurityToken token)
+ throws ProtocolException;
+
+ /*
+ * Retrieves MediaItems by users and groups.
+ *
+ * @param userIds Identifies the users that this request is relative to
+ * @param groupId Identifies the users' groups to retrieve MediaItems from
+ * @param appId Identifies the application to retrieve MediaItems from
+ * @param fields The fields to return; empty set implies all
+ * @param options Sorting/filtering/pagination options
+ * @param token A valid SecurityToken
+ *
+ * @return a response item with the requested MediaItems
+ */
+ Future<RestfulCollection<MediaItem>> getMediaItems(Set<UserId> userIds,
+ GroupId groupId, String appId, Set<String> fields,
+ CollectionOptions options, SecurityToken token)
+ throws ProtocolException;
+
+ /*
+ * Deletes a MediaItem by ID.
+ *
+ * @param userId Identifies the owner of the MediaItem to delete
+ * @param appId Identifies the application hosting the MediaItem
+ * @param albumId Identifies the parent album of the MediaItem
+ * @param mediaItemId Identifies the MediaItem to delete
+ * @param token A valid SecurityToken
+ *
+ * @return a response item containing any errors
+ */
+ Future<Void> deleteMediaItem(UserId userId, String appId, String albumId,
+ String mediaItemId, SecurityToken token) throws ProtocolException;
+
+ /*
+ * Create a MediaItem in the given album for the given user.
+ *
+ * @param userId Identifies the owner of the MediaItem to create
+ * @param appId Identifies the application hosting the MediaItem
+ * @param albumId Identifies the album to contain the MediaItem
+ * @param mediaItem The MediaItem to create
+ * @param token A valid SecurityToken
+ *
+ * @return a response containing any errors
+ */
+ Future<Void> createMediaItem(UserId userId, String appId, String albumId,
+ MediaItem mediaItem, SecurityToken token) throws ProtocolException;
+
+ /*
+ * Updates a MediaItem for the given user. The MediaItem ID specified in
+ * the REST end-point is used, even if the MediaItem also defines an ID.
+ *
+ * @param userId Identifies the owner of the MediaItem to update
+ * @param appId Identifies the application hosting the MediaItem
+ * @param albumId Identifies the album containing the MediaItem
+ * @param mediaItemId Identifies the MediaItem to update
+ * @param mediaItem The updated MediaItem to persist
+ * @param token A valid SecurityToken
+ *
+ * @return a response containing any errors
+ */
+ Future<Void> updateMediaItem(UserId userId, String appId, String albumId,
+ String mediaItemId, MediaItem mediaItem, SecurityToken token)
+ throws ProtocolException;
+}
Modified: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/SampleModule.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/SampleModule.java?rev=990115&r1=990114&r2=990115&view=diff
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/SampleModule.java (original)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/SampleModule.java Fri Aug 27 12:00:52 2010
@@ -19,7 +19,9 @@ package org.apache.shindig.social.sample
import org.apache.shindig.social.opensocial.oauth.OAuthDataStore;
import org.apache.shindig.social.opensocial.spi.ActivityService;
+import org.apache.shindig.social.opensocial.spi.AlbumService;
import org.apache.shindig.social.opensocial.spi.AppDataService;
+import org.apache.shindig.social.opensocial.spi.MediaItemService;
import org.apache.shindig.social.opensocial.spi.MessageService;
import org.apache.shindig.social.opensocial.spi.PersonService;
import org.apache.shindig.social.sample.oauth.SampleOAuthDataStore;
@@ -41,10 +43,11 @@ public class SampleModule extends Abstra
bind(String.class).annotatedWith(Names.named("shindig.canonical.json.db"))
.toInstance("sampledata/canonicaldb.json");
bind(ActivityService.class).to(JsonDbOpensocialService.class);
+ bind(AlbumService.class).to(JsonDbOpensocialService.class);
+ bind(MediaItemService.class).to(JsonDbOpensocialService.class);
bind(AppDataService.class).to(JsonDbOpensocialService.class);
bind(PersonService.class).to(JsonDbOpensocialService.class);
bind(MessageService.class).to(JsonDbOpensocialService.class);
-
bind(OAuthDataStore.class).to(SampleOAuthDataStore.class);
}
}
Modified: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialService.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialService.java?rev=990115&r1=990114&r2=990115&view=diff
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialService.java (original)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialService.java Fri Aug 27 12:00:52 2010
@@ -18,6 +18,16 @@
package org.apache.shindig.social.sample.spi;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Future;
+
+import javax.servlet.http.HttpServletResponse;
+
import org.apache.commons.io.IOUtils;
import org.apache.shindig.auth.SecurityToken;
import org.apache.shindig.common.util.ImmediateFuture;
@@ -28,13 +38,17 @@ import org.apache.shindig.protocol.Restf
import org.apache.shindig.protocol.conversion.BeanConverter;
import org.apache.shindig.protocol.model.SortOrder;
import org.apache.shindig.social.opensocial.model.Activity;
+import org.apache.shindig.social.opensocial.model.Album;
+import org.apache.shindig.social.opensocial.model.MediaItem;
import org.apache.shindig.social.opensocial.model.Message;
import org.apache.shindig.social.opensocial.model.MessageCollection;
import org.apache.shindig.social.opensocial.model.Person;
import org.apache.shindig.social.opensocial.spi.ActivityService;
+import org.apache.shindig.social.opensocial.spi.AlbumService;
import org.apache.shindig.social.opensocial.spi.AppDataService;
import org.apache.shindig.social.opensocial.spi.CollectionOptions;
import org.apache.shindig.social.opensocial.spi.GroupId;
+import org.apache.shindig.social.opensocial.spi.MediaItemService;
import org.apache.shindig.social.opensocial.spi.MessageService;
import org.apache.shindig.social.opensocial.spi.PersonService;
import org.apache.shindig.social.opensocial.spi.UserId;
@@ -42,16 +56,6 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Future;
-
-import javax.servlet.http.HttpServletResponse;
-
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -65,7 +69,7 @@ import com.google.inject.name.Named;
*/
@Singleton
public class JsonDbOpensocialService implements ActivityService, PersonService, AppDataService,
- MessageService {
+ MessageService, AlbumService, MediaItemService {
private static final Comparator<Person> NAME_COMPARATOR = new Comparator<Person>() {
public int compare(Person person, Person person1) {
@@ -94,6 +98,16 @@ public class JsonDbOpensocialService imp
* db["people"] -> Map<Person.Id, Array<Activity>>
*/
private static final String ACTIVITIES_TABLE = "activities";
+
+ /**
+ * db["people"] -> Map<Person.Id, Array<Album>>
+ */
+ private static final String ALBUMS_TABLE = "albums";
+
+ /**
+ * db["people"] -> Map<Person.Id, Array<MediaItem>>
+ */
+ private static final String MEDIAITEMS_TABLE = "mediaItems";
/**
* db["data"] -> Map<Person.Id, Map<String, String>>
@@ -247,6 +261,7 @@ public class JsonDbOpensocialService imp
jsonArray = new JSONArray();
db.getJSONObject(ACTIVITIES_TABLE).put(userId.getUserId(token), jsonArray);
}
+ // TODO (woodser): if used with PUT, duplicate activity would be created?
jsonArray.put(jsonObject);
return ImmediateFuture.newInstance(null);
} catch (JSONException je) {
@@ -623,19 +638,490 @@ public class JsonDbOpensocialService imp
}
return ids;
}
-
- private JSONObject convertFromActivity(Activity activity, Set<String> fields)
- throws JSONException {
- // TODO Not using fields yet
- return new JSONObject(converter.convertToString(activity));
- }
-
- public <T> T filterFields(JSONObject object, Set<String> fields, Class<T> clz)
- throws JSONException {
- if (!fields.isEmpty()) {
- // Create a copy with just the specified fields
- object = new JSONObject(object, fields.toArray(new String[fields.size()]));
- }
- return converter.convertToObject(object.toString(), clz);
- }
+
+ // TODO: not using appId
+ public Future<Album> getAlbum(UserId userId, String appId, Set<String> fields,
+ String albumId, SecurityToken token) throws ProtocolException {
+ try {
+ // First ensure user has a table
+ String user = userId.getUserId((token));
+ if (db.getJSONObject(ALBUMS_TABLE).has(user)) {
+ // Retrieve user's albums
+ JSONArray userAlbums = db.getJSONObject(ALBUMS_TABLE).getJSONArray(user);
+
+ // Search albums for given ID and owner
+ JSONObject album;
+ for (int i = 0; i < userAlbums.length(); i++) {
+ album = userAlbums.getJSONObject(i);
+ if (album.getString(Album.Field.ID.toString()).equals(albumId) &&
+ album.getString(Album.Field.OWNER_ID.toString()).equals(user)) {
+ return ImmediateFuture.newInstance(filterFields(album, fields, Album.class));
+ }
+ }
+ }
+
+ // Album wasn't found
+ throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "Album ID " + albumId + " does not exist");
+ } catch (JSONException je) {
+ throw new ProtocolException(
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ je.getMessage(), je);
+ }
+ }
+
+ // TODO: not using appId
+ public Future<RestfulCollection<Album>> getAlbums(UserId userId, String appId,
+ Set<String> fields, CollectionOptions options, Set<String> albumIds,
+ SecurityToken token) throws ProtocolException {
+ try {
+ // Ensure user has a table
+ String user = userId.getUserId(token);
+ if (db.getJSONObject(ALBUMS_TABLE).has(user)) {
+ // Get user's albums
+ JSONArray userAlbums = db.getJSONObject(ALBUMS_TABLE).getJSONArray(user);
+
+ // Stores target albums
+ List<Album> result = Lists.newArrayList();
+
+ // Search for every albumId
+ boolean found;
+ JSONObject curAlbum;
+ for (String albumId : albumIds) {
+ // Search albums for this albumId
+ found = false;
+ for (int i = 0; i < userAlbums.length(); i++) {
+ curAlbum = userAlbums.getJSONObject(i);
+ if (curAlbum.getString(Album.Field.ID.toString()).equals(albumId) &&
+ curAlbum.getString(Album.Field.OWNER_ID.toString()).equals(user)) {
+ result.add(filterFields(curAlbum, fields, Album.class));
+ found = true;
+ break;
+ }
+ }
+
+ // Error - albumId not found
+ if (!found) {
+ throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "Album ID " + albumId + " does not exist");
+ }
+ }
+
+ // Return found albums
+ return ImmediateFuture.newInstance(new RestfulCollection<Album>(result));
+ }
+
+ // Album table doesn't exist for user
+ throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "User '" + user + "' has no albums");
+ } catch (JSONException je) {
+ throw new ProtocolException(
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ je.getMessage(), je);
+ }
+ }
+
+ // TODO: not using appId
+ public Future<RestfulCollection<Album>> getAlbums(Set<UserId> userIds,
+ GroupId groupId, String appId, Set<String> fields,
+ CollectionOptions options, SecurityToken token)
+ throws ProtocolException {
+ try {
+ List<Album> result = Lists.newArrayList();
+ Set<String> idSet = getIdSet(userIds, groupId, token);
+
+ // Gather albums for all user IDs
+ for (String id : idSet) {
+ if (db.getJSONObject(ALBUMS_TABLE).has(id)) {
+ JSONArray userAlbums = db.getJSONObject(ALBUMS_TABLE).getJSONArray(id);
+ for (int i = 0; i < userAlbums.length(); i++) {
+ JSONObject album = userAlbums.getJSONObject(i);
+ if (album.getString(Album.Field.OWNER_ID.toString()).equals(id)) {
+ result.add(filterFields(album, fields, Album.class));
+ }
+ }
+ }
+ }
+ return ImmediateFuture.newInstance(new RestfulCollection<Album>(result));
+ } catch (JSONException je) {
+ throw new ProtocolException(
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ je.getMessage(), je);
+ }
+ }
+
+ // TODO: not using appId
+ public Future<Void> deleteAlbum(UserId userId, String appId, String albumId,
+ SecurityToken token) throws ProtocolException {
+ try {
+ boolean targetFound = false; // indicates if target album is found
+ JSONArray newAlbums = new JSONArray(); // list of albums minus target
+ String user = userId.getUserId(token); // retrieve user id
+
+ // First ensure user has a table
+ if (db.getJSONObject(ALBUMS_TABLE).has(user)) {
+ // Get user's albums
+ JSONArray userAlbums = db.getJSONObject(ALBUMS_TABLE).getJSONArray(user);
+
+ // Compose new list of albums excluding album to be deleted
+ JSONObject curAlbum;
+ for (int i = 0; i < userAlbums.length(); i++) {
+ curAlbum = userAlbums.getJSONObject(i);
+ if (curAlbum.getString(Album.Field.ID.toString()).equals(albumId)) {
+ targetFound = true;
+ } else {
+ newAlbums.put(curAlbum);
+ }
+ }
+ }
+
+ // Overwrite user's albums with updated list if album found
+ if (targetFound) {
+ db.getJSONObject(ALBUMS_TABLE).put(user, newAlbums);
+ return ImmediateFuture.newInstance(null);
+ } else {
+ throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "Album ID " + albumId + " does not exist");
+ }
+ } catch (JSONException je) {
+ throw new ProtocolException(
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ je.getMessage(), je);
+ }
+ }
+
+ // TODO: userId and album's ownerId don't have to match - potential problem
+ // TODO: not using appId
+ public Future<Void> createAlbum(UserId userId, String appId, Album album,
+ SecurityToken token) throws ProtocolException {
+ try {
+ // Get table of user's albums
+ String user = userId.getUserId(token);
+ JSONArray userAlbums = db.getJSONObject(ALBUMS_TABLE).getJSONArray(user);
+ if (userAlbums == null) {
+ userAlbums = new JSONArray();
+ db.getJSONObject(ALBUMS_TABLE).put(user, userAlbums);
+ }
+
+ // Convert album to JSON and set ID & owner
+ JSONObject jsonAlbum = convertToJson(album);
+ if (!jsonAlbum.has(Album.Field.ID.toString())) {
+ jsonAlbum.put(Album.Field.ID.toString(), System.currentTimeMillis());
+ }
+ if (!jsonAlbum.has(Album.Field.OWNER_ID.toString())) {
+ jsonAlbum.put(Album.Field.OWNER_ID.toString(), user);
+ }
+
+ // Insert new album into table
+ userAlbums.put(jsonAlbum);
+ return ImmediateFuture.newInstance(null);
+ } catch (JSONException je) {
+ throw new ProtocolException(
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ je.getMessage(), je);
+ }
+ }
+
+ // TODO: not using appId
+ public Future<Void> updateAlbum(UserId userId, String appId, Album album,
+ String albumId, SecurityToken token) throws ProtocolException {
+ try {
+ // First ensure user has a table
+ String user = userId.getUserId(token);
+ if (db.getJSONObject(ALBUMS_TABLE).has(user)) {
+ // Retrieve user's albums
+ JSONArray userAlbums = db.getJSONObject(ALBUMS_TABLE).getJSONArray(user);
+
+ // Convert album to JSON and set ID
+ JSONObject jsonAlbum = convertToJson(album);
+ jsonAlbum.put(Album.Field.ID.toString(), albumId);
+
+ // Iterate through albums to identify album to update
+ JSONObject curAlbum = null;
+ for (int i = 0; i < userAlbums.length(); i++) {
+ curAlbum = userAlbums.getJSONObject(i);
+ if (curAlbum.getString(Album.Field.ID.toString()).equals(albumId)) {
+ userAlbums.put(i, jsonAlbum);
+ return ImmediateFuture.newInstance(null);
+ }
+ }
+ }
+
+ // Error - no album found to update with given ID
+ throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "Album ID " + albumId + " does not exist");
+ } catch (JSONException je) {
+ throw new ProtocolException(
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ je.getMessage(), je);
+ }
+ }
+
+ // TODO: not using appId
+ public Future<MediaItem> getMediaItem(UserId userId, String appId,
+ String albumId, String mediaItemId, Set<String> fields,
+ SecurityToken token) throws ProtocolException {
+ try {
+ // First ensure user has a table
+ String user = userId.getUserId((token));
+ if (db.getJSONObject(MEDIAITEMS_TABLE).has(user)) {
+ // Retrieve user's MediaItems
+ JSONArray userMediaItems = db.getJSONObject(MEDIAITEMS_TABLE).getJSONArray(user);
+
+ // Search user's MediaItems for given ID and album
+ JSONObject mediaItem;
+ for (int i = 0; i < userMediaItems.length(); i++) {
+ mediaItem = userMediaItems.getJSONObject(i);
+ if (mediaItem.getString(MediaItem.Field.ID.toString()).equals(mediaItemId) &&
+ mediaItem.getString(MediaItem.Field.ALBUM_ID.toString()).equals(albumId)) {
+ return ImmediateFuture.newInstance(filterFields(mediaItem, fields, MediaItem.class));
+ }
+ }
+ }
+
+ // MediaItem wasn't found
+ throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "MediaItem ID '" + mediaItemId + "' does not exist within Album '" + albumId + "'");
+ } catch (JSONException je) {
+ throw new ProtocolException(
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ je.getMessage(), je);
+ }
+ }
+
+ // TODO: not using appId
+ public Future<RestfulCollection<MediaItem>> getMediaItems(UserId userId,
+ String appId, String albumId, Set<String> mediaItemIds,
+ Set<String> fields, CollectionOptions options, SecurityToken token)
+ throws ProtocolException {
+ try {
+ // Ensure user has a table
+ String user = userId.getUserId(token);
+ if (db.getJSONObject(MEDIAITEMS_TABLE).has(user)) {
+ // Get user's MediaItems
+ JSONArray userMediaItems = db.getJSONObject(MEDIAITEMS_TABLE).getJSONArray(user);
+
+ // Stores found MediaItems
+ List<MediaItem> result = Lists.newArrayList();
+
+ // Search for every MediaItem ID target
+ boolean found;
+ JSONObject curMediaItem;
+ for (String mediaItemId : mediaItemIds) {
+ // Search existing MediaItems for this MediaItem ID
+ found = false;
+ for (int i = 0; i < userMediaItems.length(); i++) {
+ curMediaItem = userMediaItems.getJSONObject(i);
+ if (curMediaItem.getString(MediaItem.Field.ID.toString()).equals(albumId) &&
+ curMediaItem.getString(MediaItem.Field.ALBUM_ID.toString()).equals(albumId)) {
+ result.add(filterFields(curMediaItem, fields, MediaItem.class));
+ found = true;
+ break;
+ }
+ }
+
+ // Error - MediaItem ID not found
+ if (!found) {
+ throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "MediaItem ID " + mediaItemId + " does not exist within Album " + albumId);
+ }
+ }
+
+ // Return found MediaItems
+ return ImmediateFuture.newInstance(new RestfulCollection<MediaItem>(result));
+ }
+
+ // Table doesn't exist for user
+ throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "MediaItem table not found for user " + user);
+ } catch (JSONException je) {
+ throw new ProtocolException(
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ je.getMessage(), je);
+ }
+ }
+
+ // TODO: not using appId
+ public Future<RestfulCollection<MediaItem>> getMediaItems(UserId userId,
+ String appId, String albumId, Set<String> fields,
+ CollectionOptions options, SecurityToken token)
+ throws ProtocolException {
+ try {
+ // First ensure user has a table
+ String user = userId.getUserId((token));
+ if (db.getJSONObject(MEDIAITEMS_TABLE).has(user)) {
+ // Retrieve user's MediaItems
+ JSONArray userMediaItems = db.getJSONObject(MEDIAITEMS_TABLE).getJSONArray(user);
+
+ // Stores target MediaItems
+ List<MediaItem> result = Lists.newArrayList();
+
+ // Search user's MediaItems for given album
+ JSONObject curMediaItem;
+ for (int i = 0; i < userMediaItems.length(); i++) {
+ curMediaItem = userMediaItems.getJSONObject(i);
+ if (curMediaItem.getString(MediaItem.Field.ALBUM_ID.toString()).equals(albumId)) {
+ result.add(filterFields(curMediaItem, fields, MediaItem.class));
+ }
+ }
+
+ // Return found MediaItems
+ return ImmediateFuture.newInstance(new RestfulCollection<MediaItem>(result));
+ }
+
+ // Album wasn't found
+ throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "Album ID " + albumId + " does not exist");
+ } catch (JSONException je) {
+ throw new ProtocolException(
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ je.getMessage(), je);
+ }
+ }
+
+ // TODO: not using appId
+ public Future<RestfulCollection<MediaItem>> getMediaItems(
+ Set<UserId> userIds, GroupId groupId, String appId,
+ Set<String> fields, CollectionOptions options, SecurityToken token)
+ throws ProtocolException {
+ try {
+ List<MediaItem> result = Lists.newArrayList();
+ Set<String> idSet = getIdSet(userIds, groupId, token);
+
+ // Gather MediaItems for all user IDs
+ for (String id : idSet) {
+ if (db.getJSONObject(MEDIAITEMS_TABLE).has(id)) {
+ JSONArray userMediaItems = db.getJSONObject(MEDIAITEMS_TABLE).getJSONArray(id);
+ for (int i = 0; i < userMediaItems.length(); i++) {
+ result.add(filterFields(userMediaItems.getJSONObject(i), fields, MediaItem.class));
+ }
+ }
+ }
+ return ImmediateFuture.newInstance(new RestfulCollection<MediaItem>(result));
+ } catch (JSONException je) {
+ throw new ProtocolException(
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ je.getMessage(), je);
+ }
+ }
+
+ // TODO: not using appId
+ public Future<Void> deleteMediaItem(UserId userId, String appId,
+ String albumId, String mediaItemId, SecurityToken token)
+ throws ProtocolException {
+ try {
+ boolean targetFound = false; // indicates if target MediaItem is found
+ JSONArray newMediaItems = new JSONArray(); // list of MediaItems minus target
+ String user = userId.getUserId(token); // retrieve user id
+
+ // First ensure user has a table
+ if (db.getJSONObject(MEDIAITEMS_TABLE).has(user)) {
+ // Get user's MediaItems
+ JSONArray userMediaItems = db.getJSONObject(MEDIAITEMS_TABLE).getJSONArray(user);
+
+ // Compose new list of MediaItems excluding item to be deleted
+ JSONObject curMediaItem;
+ for (int i = 0; i < userMediaItems.length(); i++) {
+ curMediaItem = userMediaItems.getJSONObject(i);
+ if (curMediaItem.getString(MediaItem.Field.ID.toString()).equals(mediaItemId) &&
+ curMediaItem.getString(MediaItem.Field.ALBUM_ID.toString()).equals(albumId)) {
+ targetFound = true;
+ } else {
+ newMediaItems.put(curMediaItem);
+ }
+ }
+ }
+
+ // Overwrite user's MediaItems with updated list if target found
+ if (targetFound) {
+ db.getJSONObject(MEDIAITEMS_TABLE).put(user, newMediaItems);
+ return ImmediateFuture.newInstance(null);
+ } else {
+ throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "MediaItem ID " + mediaItemId + " does not exist existin within Album " + albumId);
+ }
+ } catch (JSONException je) {
+ throw new ProtocolException(
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ je.getMessage(), je);
+ }
+ }
+
+ // TODO: not using appId
+ public Future<Void> createMediaItem(UserId userId, String appId,
+ String albumId, MediaItem mediaItem, SecurityToken token)
+ throws ProtocolException {
+ try {
+ // Get table of user's MediaItems
+ JSONArray userMediaItems = db.getJSONObject(MEDIAITEMS_TABLE).getJSONArray(userId.getUserId(token));
+ if (userMediaItems == null) {
+ userMediaItems = new JSONArray();
+ db.getJSONObject(MEDIAITEMS_TABLE).put(userId.getUserId(token), userMediaItems);
+ }
+
+ // Convert MediaItem to JSON and set ID & Album ID
+ JSONObject jsonMediaItem = convertToJson(mediaItem);
+ jsonMediaItem.put(MediaItem.Field.ALBUM_ID.toString(), albumId);
+ if (!jsonMediaItem.has(MediaItem.Field.ID.toString())) {
+ jsonMediaItem.put(MediaItem.Field.ID.toString(), System.currentTimeMillis());
+ }
+
+ // Insert new MediaItem into table
+ userMediaItems.put(jsonMediaItem);
+ return ImmediateFuture.newInstance(null);
+ } catch (JSONException je) {
+ throw new ProtocolException(
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ je.getMessage(), je);
+ }
+ }
+
+ // TODO: not using appId
+ public Future<Void> updateMediaItem(UserId userId, String appId,
+ String albumId, String mediaItemId, MediaItem mediaItem,
+ SecurityToken token) throws ProtocolException {
+ try {
+ // First ensure user has a table
+ String user = userId.getUserId(token);
+ if (db.getJSONObject(MEDIAITEMS_TABLE).has(user)) {
+ // Retrieve user's MediaItems
+ JSONArray userMediaItems = db.getJSONObject(MEDIAITEMS_TABLE).getJSONArray(user);
+
+ // Convert MediaItem to JSON and set ID & Album ID
+ JSONObject jsonMediaItem = convertToJson(mediaItem);
+ jsonMediaItem.put(MediaItem.Field.ID.toString(), mediaItemId);
+ jsonMediaItem.put(MediaItem.Field.ALBUM_ID.toString(), albumId);
+
+ // Iterate through MediaItems to identify item to update
+ JSONObject curMediaItem = null;
+ for (int i = 0; i < userMediaItems.length(); i++) {
+ curMediaItem = userMediaItems.getJSONObject(i);
+ if (curMediaItem.getString(MediaItem.Field.ID.toString()).equals(mediaItemId) &&
+ curMediaItem.getString(MediaItem.Field.ALBUM_ID.toString()).equals(albumId)) {
+ userMediaItems.put(i, jsonMediaItem);
+ return ImmediateFuture.newInstance(null);
+ }
+ }
+ }
+
+ // Error - no MediaItem found with given ID and Album ID
+ throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "MediaItem ID " + mediaItemId + " does not exist existin within Album " + albumId);
+ } catch (JSONException je) {
+ throw new ProtocolException(
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ je.getMessage(), je);
+ }
+ }
+
+ // TODO Why specifically handle Activity instead of generic POJO (below)?
+ private JSONObject convertFromActivity(Activity activity, Set<String> fields)
+ throws JSONException {
+ // TODO Not using fields yet
+ return new JSONObject(converter.convertToString(activity));
+ }
+
+ private JSONObject convertToJson(Object object) throws JSONException {
+ // TODO not using fields yet
+ return new JSONObject(converter.convertToString(object));
+ }
+
+ public <T> T filterFields(JSONObject object, Set<String> fields,
+ Class<T> clz) throws JSONException {
+ if (!fields.isEmpty()) {
+ // Create a copy with just the specified fields
+ object = new JSONObject(object, fields.toArray(new String[fields
+ .size()]));
+ }
+ return converter.convertToObject(object.toString(), clz);
+ }
}