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:58 UTC

svn commit: r990116 - in /shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social: opensocial/service/ opensocial/spi/ sample/spi/

Author: lindner
Date: Fri Aug 27 12:00:58 2010
New Revision: 990116

URL: http://svn.apache.org/viewvc?rev=990116&view=rev
Log:
SHINDIG-1410 | Spacing fixes

Modified:
    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
    shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/spi/JsonDbOpensocialService.java

Modified: 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=990116&r1=990115&r2=990116&view=diff
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/AlbumHandler.java (original)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/AlbumHandler.java Fri Aug 27 12:00:58 2010
@@ -45,141 +45,141 @@ import com.google.inject.Inject;
 @Service(name = "albums", path = "/{userId}+/{groupId}/{albumId}+")
 public class AlbumHandler {
 
-	private final AlbumService service;
-	private final ContainerConfig config;
+  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);
-	}
+  @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

Modified: 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=990116&r1=990115&r2=990116&view=diff
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/MediaItemHandler.java (original)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/MediaItemHandler.java Fri Aug 27 12:00:58 2010
@@ -28,175 +28,175 @@ import com.google.inject.Inject;
 @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);
-	}
+
+  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);
+  }
 }

Modified: 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=990116&r1=990115&r2=990116&view=diff
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/AlbumService.java (original)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/AlbumService.java Fri Aug 27 12:00:58 2010
@@ -31,91 +31,91 @@ import org.apache.shindig.social.opensoc
  */
 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;
+  /*
+    * 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

Modified: 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=990116&r1=990115&r2=990116&view=diff
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/MediaItemService.java (original)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/spi/MediaItemService.java Fri Aug 27 12:00:58 2010
@@ -13,117 +13,117 @@ import org.apache.shindig.social.opensoc
  * 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;
+
+  /*
+    * 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/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=990116&r1=990115&r2=990116&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:58 2010
@@ -638,490 +638,504 @@ public class JsonDbOpensocialService imp
     }
     return ids;
   }
-  
-  	// 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);
-	}
+
+  // 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);
+  }
 }