diff options
author | Jen Hsieh <jenhsieh@google.com> | 2009-07-06 12:57:36 -0700 |
---|---|---|
committer | Jen Hsieh <jenhsieh@google.com> | 2009-07-15 12:14:05 -0700 |
commit | 855f40bcd97a8c0668648ba300c2596669ded819 (patch) | |
tree | f36bac0d250ba38b360f76337cf852f26e43347d | |
parent | be301ed85eb8d8c36bfb75cc3478583dd69bb338 (diff) | |
download | gdata-855f40bcd97a8c0668648ba300c2596669ded819.tar.gz |
Porting detailed exception support from googlemobile.
Added new exceptions "PreconditionFailedException" for 412 and "ResourceNotModifiedException" for 304.
8 files changed, 471 insertions, 67 deletions
diff --git a/src/com/google/wireless/gdata2/ConflictDetectedException.java b/src/com/google/wireless/gdata2/ConflictDetectedException.java new file mode 100644 index 0000000..fa2d84f --- /dev/null +++ b/src/com/google/wireless/gdata2/ConflictDetectedException.java @@ -0,0 +1,32 @@ +// Copyright 2009 The Android Open Source Project. + +package com.google.wireless.gdata2; + +import com.google.wireless.gdata2.data.Entry; + +/** + * A ConflictDetectedException is thrown when the server detects a conflict + * between the Entry which the client is trying to insert or modify and an + * existing Entry. Typically this is because the version of the Entry being + * uploaded by the client is older than the version on the server, but it may + * also indicate the violation of some other constraint (e.g., key uniqueness). + */ +public class ConflictDetectedException extends GDataException { + + private final Entry conflictingEntry; + + /** + * Creates a new ConflictDetectedException with the given entry. + * @param conflictingEntry the conflicting entry state returned by the server. + */ + public ConflictDetectedException(Entry conflictingEntry) { + this.conflictingEntry = conflictingEntry; + } + + /** + * @return the conflicting Entry returned by the server. + */ + public Entry getConflictingEntry() { + return conflictingEntry; + } +} diff --git a/src/com/google/wireless/gdata2/client/AuthenticationException.java b/src/com/google/wireless/gdata2/client/AuthenticationException.java new file mode 100644 index 0000000..72caddb --- /dev/null +++ b/src/com/google/wireless/gdata2/client/AuthenticationException.java @@ -0,0 +1,37 @@ +// Copyright 2009 The Android Open Source Project. + +package com.google.wireless.gdata2.client; + +import com.google.wireless.gdata2.GDataException; + +/** + * Exception thrown when a user's credentials could not be authenticated. + */ +public class AuthenticationException extends GDataException { + + /** + * Creates a new AuthenticationException. + */ + public AuthenticationException() { + } + + /** + * Creates a new AuthenticationException with a supplied message. + * @param message The message for the exception. + */ + public AuthenticationException(String message) { + super(message); + } + + /** + * Creates a new AuthenticationException with a supplied message and + * underlying cause. + * + * @param message The message for the exception. + * @param cause Another throwable that was caught and wrapped in this + * exception. + */ + public AuthenticationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/com/google/wireless/gdata2/client/GDataServiceClient.java b/src/com/google/wireless/gdata2/client/GDataServiceClient.java index 4873020..51d0b07 100644 --- a/src/com/google/wireless/gdata2/client/GDataServiceClient.java +++ b/src/com/google/wireless/gdata2/client/GDataServiceClient.java @@ -2,6 +2,12 @@ package com.google.wireless.gdata2.client; +import com.google.wireless.gdata2.ConflictDetectedException; +import com.google.wireless.gdata2.GDataException; +import com.google.wireless.gdata2.client.AuthenticationException; +import com.google.wireless.gdata2.client.HttpException; +import com.google.wireless.gdata2.client.ResourceGoneException; +import com.google.wireless.gdata2.client.ResourceNotFoundException; import com.google.wireless.gdata2.data.Entry; import com.google.wireless.gdata2.data.MediaEntry; import com.google.wireless.gdata2.data.StringUtils; @@ -81,35 +87,61 @@ public abstract class GDataServiceClient { * @param feedUrl ThAe URL of the feed that should be fetched. * @param authToken The authentication token for this user. * @param eTag The etag used for this query. Passing null will - * result in an unconditional query + * result in an unconditional query * @return A {@link GDataParser} for the requested feed. + * @throws AuthenticationException Thrown if the server considers the + * authToken invalid. + * @throws ResourceGoneException Thrown if the server indicates that the + * resource is Gone. Currently used to indicate that some Tombstones + * are missing. + * @throws ResourceNotModifiedException Thrown if the retrieval fails + * because the specified ETag matches the current ETag of the entry + * (i.e. the entry has not been modified since last retrieval). + * @throws HttpException Thrown if the request returns an error response. * @throws ParseException Thrown if the server response cannot be parsed. * @throws IOException Thrown if an error occurs while communicating with - * the GData service. - * @throws HttpException Thrown if the http response contains a result other than 2xx + * the GData service. */ - public GDataParser getParserForFeed(Class feedEntryClass, String feedUrl, String authToken, String eTag) - throws ParseException, IOException, HttpException { - InputStream is = gDataClient.getFeedAsStream(feedUrl, authToken, eTag, getProtocolVersion()); - return gDataParserFactory.createParser(feedEntryClass, is); + public GDataParser getParserForFeed(Class feedEntryClass, String feedUrl, String authToken, + String eTag) throws GDataException, IOException { + try { + InputStream is = gDataClient.getFeedAsStream(feedUrl, authToken, eTag, + getProtocolVersion()); + return gDataParserFactory.createParser(feedEntryClass, is); + } catch (HttpException e) { + convertHttpExceptionForFeedReads("Could not fetch feed " + feedUrl, e); + return null; // never reached + } } /** - * Fetches a media entry as an InputStream. The caller is responsible for closing the - * returned {@link InputStream}. + * 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. - * @param eTag The eTag associated with this request, this will - * cause the GET to return a 304 if the content was - * not modified. - * @return A {@link InputStream} for the requested media entry. + * @param eTag The ETag associated with this request. + * @return A {@link InputStream} for the requested media entry. + * @throws AuthenticationException Thrown if the server considers the + * authToken invalid. + * @throws ResourceGoneException Thrown if the server indicates that the + * resource is Gone. Currently used to indicate that some Tombstones + * are missing. + * @throws ResourceNotModifiedException Thrown if the retrieval fails + * because the specified ETag matches the current ETag of the entry + * (i.e. the entry has not been modified since last retrieval). + * @throws HttpException Thrown if the request returns an error response. * @throws IOException Thrown if an error occurs while communicating with - * the GData service. + * the GData service. */ public InputStream getMediaEntryAsStream(String mediaEntryUrl, String authToken, String eTag) - throws IOException, HttpException { + throws GDataException, IOException { + try { return gDataClient.getMediaEntryAsStream(mediaEntryUrl, authToken, eTag, getProtocolVersion()); + } catch (HttpException e) { + convertHttpExceptionForEntryReads("Could not fetch media entry " + mediaEntryUrl, e); + return null; // never reached + } } /** @@ -120,36 +152,56 @@ public abstract class GDataServiceClient { * @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. + * provided entry. + * @throws ConflictDetectedException Thrown if the server detects an + * existing entry that conflicts with this one. + * @throws AuthenticationException Thrown if the server considers the + * authToken invalid. + * @throws HttpException Thrown if the request returns an error response. * @throws ParseException Thrown if the server response cannot be parsed. * @throws IOException Thrown if an error occurs while communicating with - * the GData service. - * @throws HttpException if the service returns an error response + * the GData service. */ public Entry createEntry(String feedUrl, String authToken, Entry entry) - throws ParseException, IOException, HttpException { - GDataSerializer serializer = gDataParserFactory.createSerializer(entry); + throws GDataException, ParseException, IOException { + GDataSerializer serializer = gDataParserFactory.createSerializer(entry); + try { InputStream is = gDataClient.createEntry(feedUrl, authToken, getProtocolVersion(), serializer); return parseEntry(entry.getClass(), is); + } catch (HttpException e) { + convertHttpExceptionForWrites(entry.getClass(), "Could not create entry " + feedUrl, 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 - * @param eTag The etag used for this query. Passing null - * will result in an unconditional query - * @throws ParseException Thrown if the server response cannot be parsed. - * @throws HttpException if the service returns an error response - * @throws IOException Thrown if an error occurs while communicating with - * the GData service. - * @return The entry returned by the server - */ + /** + * 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 + * @param eTag The etag used for this query. Passing null will result in an + * unconditional query + * @return The entry returned by the server. + * @throws AuthenticationException Thrown if the server considers the + * authToken invalid. + * @throws ResourceNotFoundException Thrown if the resource was not found. + * @throws ResourceNotModifiedException Thrown if the retrieval fails + * because the specified ETag matches the current ETag of the entry + * (i.e. the entry has not been modified since last retrieval). + * @throws HttpException Thrown if the request returns an error response. + * @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, String eTag) - throws ParseException, IOException, HttpException { + throws GDataException, ParseException, IOException { + try { InputStream is = getGDataClient().getFeedAsStream(id, authToken, eTag, getProtocolVersion()); return parseEntry(entryClass, is); + } catch (HttpException e) { + convertHttpExceptionForEntryReads("Could not fetch entry " + id, e); + return null; // never reached + } } /** @@ -159,22 +211,35 @@ public abstract class GDataServiceClient { * @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. + * provided entry. + * @throws AuthenticationException Thrown if the server considers the + * authToken invalid. + * @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. + * @throws PreconditionFailedException Thrown if the update fails because + * the specified ETag does not match the current ETag of the entry. + * @throws HttpException Thrown if the request returns an error response. * @throws ParseException Thrown if the server response cannot be parsed. * @throws IOException Thrown if an error occurs while communicating with - * the GData service. - * @throws HttpException if the service returns an error response + * the GData service. */ public Entry updateEntry(Entry entry, String authToken) - throws ParseException, IOException, HttpException { - String editUri = entry.getEditUri(); - if (StringUtils.isEmpty(editUri)) { - throw new ParseException("No edit URI -- cannot update."); - } + throws GDataException, ParseException, IOException { + String editUri = entry.getEditUri(); + if (StringUtils.isEmpty(editUri)) { + throw new ParseException("No edit URI -- cannot update."); + } - GDataSerializer serializer = gDataParserFactory.createSerializer(entry); - InputStream is = gDataClient.updateEntry(editUri, authToken, entry.getETag(), getProtocolVersion(), serializer); + GDataSerializer serializer = gDataParserFactory.createSerializer(entry); + try { + InputStream is = gDataClient.updateEntry(editUri, authToken, entry.getETag(), + getProtocolVersion(), serializer); return parseEntry(entry.getClass(), is); + } catch (HttpException e) { + convertHttpExceptionForWrites(entry.getClass(), "Could not update entry " + editUri, e); + return null; // never reached + } } /** @@ -182,27 +247,41 @@ public abstract class GDataServiceClient { * 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 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 - * @param eTag The etag used for this query. Passing null will - * result in an unconditional query - * @throws HttpException if the service returns an error response + * provided entry + * @param eTag The etag used for this query. Passing null will result in an + * unconditional query + * @throws AuthenticationException Thrown if the server considers the + * authToken invalid. + * @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. + * @throws PreconditionFailedException Thrown if the update fails because + * the specified ETag does not match the current ETag of the entry. + * @throws HttpException Thrown if the request returns an error response. * @throws ParseException Thrown if the server response cannot be parsed. * @throws IOException Thrown if an error occurs while communicating with - * the GData service. + * the GData service. */ public MediaEntry updateMediaEntry(String editUri, InputStream inputStream, String contentType, - String authToken, String eTag) throws IOException, HttpException, ParseException { - if (StringUtils.isEmpty(editUri)) { - throw new IllegalArgumentException("No edit URI -- cannot update."); - } + String authToken, String eTag) + throws GDataException, ParseException, IOException { + if (StringUtils.isEmpty(editUri)) { + throw new IllegalArgumentException("No edit URI -- cannot update."); + } - InputStream is = gDataClient.updateMediaEntry(editUri, authToken, eTag, getProtocolVersion(), inputStream, contentType); - return (MediaEntry)parseEntry(MediaEntry.class, is); + try { + InputStream is = gDataClient.updateMediaEntry(editUri, authToken, eTag, + getProtocolVersion(), inputStream, contentType); + return (MediaEntry) parseEntry(MediaEntry.class, is); + } catch (HttpException e) { + convertHttpExceptionForWrites(MediaEntry.class, "Could not update entry " + editUri, e); + return null; // never reached + } } /** @@ -210,15 +289,31 @@ public abstract class GDataServiceClient { * * @param editUri The editUri for the entry that should be deleted. * @param authToken The authentication token for this user. - * @param eTag The etag used for this query. Passing null will - * result in an unconditional query + * @param eTag The etag used for this query. Passing null will result in an + * unconditional query + * @throws AuthenticationException Thrown if the server considers the + * authToken invalid. + * @throws ConflictDetectedException Thrown if the server version of + * this entry has changed since it was retrieved. + * @throws PreconditionFailedException Thrown if the update fails because + * the specified ETag does not match the current ETag of the entry. + * @throws HttpException Thrown if the request returns an error response. + * @throws ParseException Thrown if the server response cannot be parsed. * @throws IOException Thrown if an error occurs while communicating with - * the GData service. - * @throws HttpException if the service returns an error response + * the GData service. */ public void deleteEntry(String editUri, String authToken, String eTag) - throws IOException, HttpException { + throws GDataException, ParseException, IOException { + try { gDataClient.deleteEntry(editUri, authToken, eTag); + } 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 " + editUri, e); + } } private Entry parseEntry(Class entryClass, InputStream is) throws ParseException, IOException { @@ -239,16 +334,98 @@ public abstract class GDataServiceClient { * @param batchUrl The url to which the batch is submitted. * @param authToken The authentication token for this user. * @param entries an enumeration of the entries to submit. + * @throws AuthenticationException Thrown if the server considers the + * authToken invalid. * @throws HttpException if the service returns an error response * @throws ParseException Thrown if the server response cannot be parsed. * @throws IOException Thrown if an error occurs while communicating with the - * GData service. + * GData service. */ public GDataParser submitBatch(Class feedEntryClass, String batchUrl, String authToken, - Enumeration entries) throws ParseException, IOException, HttpException { + Enumeration entries) throws GDataException, ParseException, IOException { GDataSerializer serializer = gDataParserFactory.createSerializer(entries); - InputStream is = gDataClient.submitBatch(batchUrl, authToken, getProtocolVersion(), serializer); - return gDataParserFactory.createParser(feedEntryClass, is); + try { + InputStream is = gDataClient.submitBatch(batchUrl, authToken, getProtocolVersion(), + serializer); + return gDataParserFactory.createParser(feedEntryClass, is); + } catch (HttpException e) { + convertHttpExceptionsForBatches("Could not submit batch " + batchUrl, e); + return null; // never reached. + } } + protected void convertHttpExceptionForFeedReads(String message, HttpException cause) + throws AuthenticationException, ResourceGoneException, ResourceNotModifiedException, + HttpException { + switch (cause.getStatusCode()) { + case HttpException.SC_FORBIDDEN: + case HttpException.SC_UNAUTHORIZED: + throw new AuthenticationException(message, cause); + case HttpException.SC_GONE: + throw new ResourceGoneException(message, cause); + case HttpException.SC_NOT_MODIFIED: + throw new ResourceNotModifiedException(message, cause); + default: + throw new HttpException(message + ": " + cause.getMessage(), + cause.getStatusCode(), cause.getResponseStream()); + } + } + + protected void convertHttpExceptionForEntryReads(String message, HttpException cause) + throws AuthenticationException, HttpException, ResourceNotFoundException, + ResourceNotModifiedException { + 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); + case HttpException.SC_NOT_MODIFIED: + throw new ResourceNotModifiedException(message, cause); + default: + throw new HttpException(message + ": " + cause.getMessage(), + cause.getStatusCode(), cause.getResponseStream()); + } + } + + protected void convertHttpExceptionsForBatches(String message, HttpException cause) + throws AuthenticationException, ParseException, HttpException { + switch (cause.getStatusCode()) { + case HttpException.SC_FORBIDDEN: + case HttpException.SC_UNAUTHORIZED: + throw new AuthenticationException(message, cause); + case HttpException.SC_BAD_REQUEST: + throw new ParseException(message + ": " + cause); + default: + throw new HttpException(message + ": " + cause.getMessage(), + cause.getStatusCode(), cause.getResponseStream()); + } + } + + protected void convertHttpExceptionForWrites(Class entryClass, String message, + HttpException cause) throws ConflictDetectedException, + AuthenticationException, PreconditionFailedException, ParseException, + HttpException, IOException { + switch (cause.getStatusCode()) { + case HttpException.SC_CONFLICT: + Entry entry = null; + 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); + case HttpException.SC_FORBIDDEN: + case HttpException.SC_UNAUTHORIZED: + throw new AuthenticationException(message, cause); + case HttpException.SC_PRECONDITION_FAILED: + throw new PreconditionFailedException(message, cause); + default: + throw new HttpException(message + ": " + cause.getMessage(), + cause.getStatusCode(), cause.getResponseStream()); + } + } } diff --git a/src/com/google/wireless/gdata2/client/HttpException.java b/src/com/google/wireless/gdata2/client/HttpException.java index ad5dd92..cf1475f 100644 --- a/src/com/google/wireless/gdata2/client/HttpException.java +++ b/src/com/google/wireless/gdata2/client/HttpException.java @@ -2,13 +2,17 @@ package com.google.wireless.gdata2.client; +import com.google.wireless.gdata2.GDataException; + import java.io.InputStream; /** * A class representing exceptional (i.e., non 200) responses from an HTTP * Server. */ -public class HttpException extends Exception { +public class HttpException extends GDataException { + + public static final int SC_NOT_MODIFIED = 304; public static final int SC_BAD_REQUEST = 400; @@ -22,6 +26,8 @@ public class HttpException extends Exception { public static final int SC_GONE = 410; + public static final int SC_PRECONDITION_FAILED = 412; + public static final int SC_INTERNAL_SERVER_ERROR = 500; private final int statusCode; diff --git a/src/com/google/wireless/gdata2/client/PreconditionFailedException.java b/src/com/google/wireless/gdata2/client/PreconditionFailedException.java new file mode 100644 index 0000000..caa880e --- /dev/null +++ b/src/com/google/wireless/gdata2/client/PreconditionFailedException.java @@ -0,0 +1,39 @@ +// Copyright 2009 The Android Open Source Project. + +package com.google.wireless.gdata2.client; + +import com.google.wireless.gdata2.GDataException; + +/** + * Exception thrown when an update fails because the specified ETag doesn't + * match the current ETag on the entry (which implies that the entry has changed + * on the server since it was last retrieved). + */ +public class PreconditionFailedException extends GDataException { + + /** + * Creates a new PreconditionFailedException. + */ + public PreconditionFailedException() { + } + + /** + * Creates a new PreconditionFailedException with a supplied message. + * @param message The message for the exception. + */ + public PreconditionFailedException(String message) { + super(message); + } + + /** + * Creates a new PreconditionFailedException with a supplied message and + * underlying cause. + * + * @param message The message for the exception. + * @param cause Another throwable that was caught and wrapped in this + * exception. + */ + public PreconditionFailedException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/com/google/wireless/gdata2/client/ResourceGoneException.java b/src/com/google/wireless/gdata2/client/ResourceGoneException.java new file mode 100644 index 0000000..c9911da --- /dev/null +++ b/src/com/google/wireless/gdata2/client/ResourceGoneException.java @@ -0,0 +1,37 @@ +// Copyright 2009 The Android Open Source Project. + +package com.google.wireless.gdata2.client; + +import com.google.wireless.gdata2.GDataException; + +/** + * Exception thrown when a specified resource is gone + */ +public class ResourceGoneException extends GDataException { + + /** + * Creates a new ResourceGoneException. + */ + public ResourceGoneException() { + } + + /** + * Creates a new ResourceGoneException with a supplied message. + * @param message The message for the exception. + */ + public ResourceGoneException(String message) { + super(message); + } + + /** + * Creates a new ResourceGoneException with a supplied message and + * underlying cause. + * + * @param message The message for the exception. + * @param cause Another throwable that was caught and wrapped in this + * exception. + */ + public ResourceGoneException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/com/google/wireless/gdata2/client/ResourceNotFoundException.java b/src/com/google/wireless/gdata2/client/ResourceNotFoundException.java new file mode 100644 index 0000000..766abd6 --- /dev/null +++ b/src/com/google/wireless/gdata2/client/ResourceNotFoundException.java @@ -0,0 +1,37 @@ +// Copyright 2009 The Android Open Source Project. + +package com.google.wireless.gdata2.client; + +import com.google.wireless.gdata2.GDataException; + +/** + * Exception thrown when a specified resource does not exist + */ +public class ResourceNotFoundException extends GDataException { + + /** + * Creates a new ResourceNotFoundException. + */ + public ResourceNotFoundException() { + } + + /** + * Creates a new ResourceNotFoundException with a supplied message. + * @param message The message for the exception. + */ + public ResourceNotFoundException(String message) { + super(message); + } + + /** + * Creates a new ResourceNotFoundException with a supplied message and + * underlying cause. + * + * @param message The message for the exception. + * @param cause Another throwable that was caught and wrapped in this + * exception. + */ + public ResourceNotFoundException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/com/google/wireless/gdata2/client/ResourceNotModifiedException.java b/src/com/google/wireless/gdata2/client/ResourceNotModifiedException.java new file mode 100644 index 0000000..2873bde --- /dev/null +++ b/src/com/google/wireless/gdata2/client/ResourceNotModifiedException.java @@ -0,0 +1,39 @@ +// Copyright 2009 The Android Open Source Project. + +package com.google.wireless.gdata2.client; + +import com.google.wireless.gdata2.GDataException; + +/** + * Exception thrown when a retrieval fails because the specified ETag matches + * the current ETag on the entry (which implies that the entry has not changed + * on the server since it was last retrieved). + */ +public class ResourceNotModifiedException extends GDataException { + + /** + * Creates a new ResourceNotModifiedException. + */ + public ResourceNotModifiedException() { + } + + /** + * Creates a new ResourceNotModifiedException with a supplied message. + * @param message The message for the exception. + */ + public ResourceNotModifiedException(String message) { + super(message); + } + + /** + * Creates a new ResourceNotModifiedException with a supplied message and + * underlying cause. + * + * @param message The message for the exception. + * @param cause Another throwable that was caught and wrapped in this + * exception. + */ + public ResourceNotModifiedException(String message, Throwable cause) { + super(message, cause); + } +} |