blob: 8b32885541ab8248792771f26113fd3010c1b3db [file] [log] [blame]
// Copyright 2008 The Android Open Source Project
package com.google.wireless.gdata.client;
import com.google.wireless.gdata.data.Entry;
import com.google.wireless.gdata.data.StringUtils;
import com.google.wireless.gdata.data.MediaEntry;
import com.google.wireless.gdata.ConflictDetectedException;
import com.google.wireless.gdata.parser.GDataParser;
import com.google.wireless.gdata.parser.ParseException;
import com.google.wireless.gdata.serializer.GDataSerializer;
import java.io.IOException;
import java.io.InputStream;
/**
* Abstract base class for service-specific clients to access GData feeds.
*/
public abstract class GDataServiceClient {
private final GDataClient gDataClient;
private final GDataParserFactory gDataParserFactory;
// TODO: remove this, after coordinating with other developers.
private static final boolean PARSE_CONFLICTING_ENTRIES = false;
public GDataServiceClient(GDataClient gDataClient,
GDataParserFactory gDataParserFactory) {
this.gDataClient = gDataClient;
this.gDataParserFactory = gDataParserFactory;
}
/**
* Returns the {@link GDataClient} being used by this GDataServiceClient.
* @return The {@link GDataClient} being used by this GDataServiceClient.
*/
protected GDataClient getGDataClient() {
return gDataClient;
}
/**
* Returns the {@link GDataParserFactory} being used by this
* GDataServiceClient.
* @return The {@link GDataParserFactory} being used by this
* GDataServiceClient.
*/
protected GDataParserFactory getGDataParserFactory() {
return gDataParserFactory;
}
/**
* Returns the name of the service. Used for authentication.
* @return The name of the service.
*/
public abstract String getServiceName();
/**
* Creates {@link QueryParams} that can be used to restrict the feed
* contents that are fetched.
* @return The QueryParams that can be used with this client.
*/
public QueryParams createQueryParams() {
return gDataClient.createQueryParams();
}
/**
* Fetches a feed for this user. The caller is responsible for closing the
* returned {@link GDataParser}.
*
* @param feedEntryClass the class of Entry that is contained in the feed
* @param feedUrl The URL of the feed that should be fetched.
* @param authToken The authentication token for this user.
* @return A {@link GDataParser} for the requested feed.
* @throws AuthenticationException Thrown if the server considers the
* authToken invalid.
* @throws ParseException Thrown if the server response cannot be parsed.
* @throws IOException Thrown if an error occurs while communicating with
* the GData service.
*/
public GDataParser getParserForFeed(Class feedEntryClass, String feedUrl, String authToken)
throws AuthenticationException, ParseException, IOException,
AllDeletedUnavailableException {
try {
InputStream is = gDataClient.getFeedAsStream(feedUrl, authToken);
return gDataParserFactory.createParser(feedEntryClass, is);
} catch (HttpException e) {
convertHttpExceptionForReads("Could not fetch feed.", e);
return null; // never reached
}
}
/**
* Fetches a media entry as an InputStream. The caller is responsible for closing the
* returned {@link InputStream}.
*
* @param mediaEntryUrl The URL of the media entry that should be fetched.
* @param authToken The authentication token for this user.
* @return A {@link InputStream} for the requested media entry.
* @throws AuthenticationException Thrown if the server considers the
* authToken invalid.
* @throws IOException Thrown if an error occurs while communicating with
* the GData service.
*/
public InputStream getMediaEntryAsStream(String mediaEntryUrl, String authToken)
throws AuthenticationException, IOException, ResourceNotFoundException {
try {
return gDataClient.getMediaEntryAsStream(mediaEntryUrl, authToken);
} catch (HttpException e) {
convertHttpExceptionForMediaEntries("Could not fetch media entry " + mediaEntryUrl, e);
return null; // never reached
}
}
/**
* Creates a new entry at the provided feed. Parses the server response
* into the version of the entry stored on the server.
*
* @param feedUrl The feed where the entry should be created.
* @param authToken The authentication token for this user.
* @param entry The entry that should be created.
* @return The entry returned by the server as a result of creating the
* provided entry.
* @throws AuthenticationException Thrown if the server considers the
* authToken invalid.
* @throws ParseException Thrown if the server response cannot be parsed.
* @throws IOException Thrown if an error occurs while communicating with
* the GData service.
* @throws ConflictDetectedException Thrown if the server detects an
* existing entry that conflicts with this one.
*/
public Entry createEntry(String feedUrl, String authToken, Entry entry)
throws AuthenticationException, ParseException, IOException,
ConflictDetectedException {
GDataSerializer serializer = gDataParserFactory.createSerializer(entry);
try {
InputStream is = gDataClient.createEntry(feedUrl, authToken, serializer);
return parseEntry(entry.getClass(), is);
} catch (HttpException e) {
convertHttpExceptionForWrites(entry.getClass(), "Could not create entry.", e);
return null; // never reached.
}
}
/**
* Fetches an existing entry.
* @param entryClass the type of entry to expect
* @param id of the entry to fetch.
* @param authToken The authentication token for this user. @return The entry returned by the server.
* @throws AuthenticationException Thrown if the server considers the
* authToken invalid.
* @throws ParseException Thrown if the server response cannot be parsed.
* @throws IOException Thrown if an error occurs while communicating with
* the GData service.
*/
public Entry getEntry(Class entryClass, String id, String authToken)
throws AuthenticationException, ParseException, IOException,
AllDeletedUnavailableException {
try {
InputStream is = getGDataClient().getFeedAsStream(id, authToken);
return parseEntry(entryClass, is);
} catch (HttpException e) {
convertHttpExceptionForReads("Could not fetch entry.", e);
return null; // never reached
}
}
/**
* Updates an existing entry. Parses the server response into the version
* of the entry stored on the server.
*
* @param entry The entry that should be updated.
* @param authToken The authentication token for this user.
* @return The entry returned by the server as a result of updating the
* provided entry.
* @throws AuthenticationException Thrown if the server considers the
* authToken invalid.
* @throws ParseException Thrown if the server response cannot be parsed.
* @throws IOException Thrown if an error occurs while communicating with
* the GData service.
* @throws ConflictDetectedException Thrown if the server detects an
* existing entry that conflicts with this one, or if the server version of
* this entry has changed since it was retrieved.
*/
public Entry updateEntry(Entry entry, String authToken)
throws AuthenticationException, ParseException, IOException,
ConflictDetectedException {
String editUri = entry.getEditUri();
if (StringUtils.isEmpty(editUri)) {
throw new ParseException("No edit URI -- cannot update.");
}
GDataSerializer serializer = gDataParserFactory.createSerializer(entry);
try {
InputStream is = gDataClient.updateEntry(editUri,
authToken,
serializer);
return parseEntry(entry.getClass(), is);
} catch (HttpException e) {
if (e.getStatusCode() == HttpException.SC_NOT_FOUND) {
throw new ParseException("Could not update entry.", e);
}
convertHttpExceptionForWrites(entry.getClass(), "Could not update entry.", e);
return null; // never reached
}
}
/**
* Updates an existing entry. Parses the server response into the metadata
* of the entry stored on the server.
*
* @param editUri The URI of the resource that should be updated.
* @param inputStream The {@link java.io.InputStream} that contains the new value
* of the media entry
* @param contentType The content type of the new media entry
* @param authToken The authentication token for this user.
* @return The entry returned by the server as a result of updating the
* provided entry.
* @throws AuthenticationException Thrown if the server considers the
* authToken invalid.
* @throws ParseException Thrown if the server response cannot be parsed.
* @throws IOException Thrown if an error occurs while communicating with
* the GData service.
* @throws ConflictDetectedException Thrown if the server detects an
* existing entry that conflicts with this one, or if the server version of
* this entry has changed since it was retrieved.
*/
public MediaEntry updateMediaEntry(String editUri, InputStream inputStream,
String contentType,
String authToken)
throws AuthenticationException, ParseException, IOException,
ConflictDetectedException {
if (StringUtils.isEmpty(editUri)) {
throw new ParseException("No edit URI -- cannot update.");
}
try {
InputStream is = gDataClient.updateMediaEntry(editUri, authToken,
inputStream, contentType);
return (MediaEntry)parseEntry(MediaEntry.class, is);
} catch (HttpException e) {
convertHttpExceptionForWrites(MediaEntry.class, "Could not update entry.", e);
return null; // never reached
}
}
/**
* Deletes an existing entry.
*
* @param editUri The editUri for the entry that should be deleted.
* @param authToken The authentication token for this user.
* @throws AuthenticationException Thrown if the server considers the
* authToken invalid.
* @throws ParseException Thrown if the server response cannot be parsed.
* @throws IOException Thrown if an error occurs while communicating with
* the GData service.
* @throws ConflictDetectedException Thrown if the server version of
* this entry has changed since it was retrieved.
*/
public void deleteEntry(String editUri, String authToken)
throws AuthenticationException, ConflictDetectedException,
ParseException, IOException {
try {
gDataClient.deleteEntry(editUri, authToken);
} catch (HttpException e) {
if (e.getStatusCode() == HttpException.SC_NOT_FOUND) {
// the server does not know about this entry.
// nothing to delete.
return;
}
convertHttpExceptionForWrites(null, "Unable to delete", e);
}
}
private Entry parseEntry(Class entryClass, InputStream is) throws ParseException, IOException {
GDataParser parser = null;
try {
parser = gDataParserFactory.createParser(entryClass, is);
return parser.parseStandaloneEntry();
} finally {
if (parser != null) {
parser.close();
}
}
}
protected void convertHttpExceptionForReads(String message, HttpException cause)
throws AuthenticationException, IOException, AllDeletedUnavailableException {
switch (cause.getStatusCode()) {
case HttpException.SC_FORBIDDEN:
case HttpException.SC_UNAUTHORIZED:
throw new AuthenticationException(message, cause);
case HttpException.SC_GONE:
throw new AllDeletedUnavailableException(message, cause);
default:
throw new IOException(message + ": " + cause.getMessage());
}
}
protected void convertHttpExceptionForMediaEntries(String message, HttpException cause)
throws AuthenticationException, IOException, ResourceNotFoundException {
switch (cause.getStatusCode()) {
case HttpException.SC_FORBIDDEN:
case HttpException.SC_UNAUTHORIZED:
throw new AuthenticationException(message, cause);
case HttpException.SC_NOT_FOUND:
throw new ResourceNotFoundException(message, cause);
default:
throw new IOException(message + ": " + cause.getMessage());
}
}
protected void convertHttpExceptionForWrites(Class entryClass, String message,
HttpException cause) throws ConflictDetectedException,
AuthenticationException, ParseException, IOException {
switch (cause.getStatusCode()) {
case HttpException.SC_CONFLICT:
Entry entry = null;
if (PARSE_CONFLICTING_ENTRIES) {
if (entryClass != null) {
InputStream is = cause.getResponseStream();
if (is != null) {
entry = parseEntry(entryClass, cause.getResponseStream());
}
}
}
throw new ConflictDetectedException(entry);
case HttpException.SC_BAD_REQUEST:
throw new ParseException(message + ": " + cause.getMessage());
case HttpException.SC_FORBIDDEN:
case HttpException.SC_UNAUTHORIZED:
throw new AuthenticationException(message, cause);
default:
throw new IOException(message + ": " + cause.getMessage());
}
}
}