2 ;;;; Copyright (c) 2008 Zachary Beane, All Rights Reserved
4 ;;;; Redistribution and use in source and binary forms, with or without
5 ;;;; modification, are permitted provided that the following conditions
8 ;;;; * Redistributions of source code must retain the above copyright
9 ;;;; notice, this list of conditions and the following disclaimer.
11 ;;;; * Redistributions in binary form must reproduce the above
12 ;;;; copyright notice, this list of conditions and the following
13 ;;;; disclaimer in the documentation and/or other materials
14 ;;;; provided with the distribution.
16 ;;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
17 ;;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 ;;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 ;;;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20 ;;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 ;;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
22 ;;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 ;;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 ;;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 ;;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 ;;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 (defvar *error-binder
*
36 ("Message" (bind :message
))
37 (elements-alist :data
))))
39 (defclass amazon-error
(response)
48 :accessor error-data
)))
50 (set-element-class "Error" 'amazon-error
)
52 (defgeneric error-data-value
(name instance
)
53 (:method
(name (response amazon-error
))
54 (cdr (assoc name
(error-data response
) :test
#'equalp
))))
56 (defmethod specialized-initialize ((response amazon-error
) source
)
57 (let ((bindings (xml-bind *error-binder
* source
)))
58 (setf (code response
) (bvalue :code bindings
))
59 (setf (message response
) (bvalue :message bindings
))
60 (setf (error-data response
) (bvalue :data bindings
))))
62 (defmethod print-object ((response amazon-error
) stream
)
63 (print-unreadable-object (response stream
:type t
)
64 (prin1 (code response
) stream
)))
66 ;;; Further specializing error messages/conditions
68 (defun report-request-error (condition stream
)
69 (format stream
"~A: ~A"
70 (code (request-error-response condition
))
71 (message (request-error-response condition
))))
73 (define-condition request-error
(error)
76 :reader request-error-request
)
79 :reader request-error-response
)
82 :reader request-error-data
))
83 (:report report-request-error
))
85 (defparameter *specific-errors
* (make-hash-table :test
'equalp
))
87 (defun specific-error (amazon-code)
88 (gethash amazon-code
*specific-errors
* 'request-error
))
90 (defgeneric signal-specific-error
(response condition-name
)
91 (:method
(response condition-name
)
93 :request
(request response
)
95 :data
(error-data response
))))
97 (defgeneric maybe-signal-error
(response)
100 (:method
((response amazon-error
))
101 (signal-specific-error response
(specific-error (code response
)))))
103 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
104 (defun error-reader-name (suffix)
105 (intern (concatenate 'string
(symbol-name 'request-error
)
107 (symbol-name suffix
))
110 (defmacro define-specific-error
((condition-name code
)
113 (labels ((slot-name (slot)
118 (keywordify (slot-name slot
)))
119 (slot-definition (slot)
121 :initarg
,(slot-keyword slot
)
122 :reader
,(error-reader-name (slot-name slot
))))
123 (slot-initializer (slot)
124 (list (slot-keyword slot
)
125 `(error-data-value ,(slot-code slot
) response
))))
127 (setf (gethash ,code
*specific-errors
*) ',condition-name
)
128 (define-condition ,condition-name
(,@superclasses request-error
)
129 ,(mapcar #'slot-definition slots
)
131 (defmethod signal-specific-error ((response amazon-error
)
132 (condition-name (eql ',condition-name
)))
133 (error ',condition-name
134 :request
(request response
)
136 :data
(error-data response
)
137 ,@(mapcan #'slot-initializer slots
))))))
140 ;;; The specific errors
142 (define-specific-error (internal-error "InternalError") () ())
144 (define-specific-error (slow-down "SlowDown") () ())
146 (define-specific-error (no-such-bucket "NoSuchBucket") ()
147 ((bucket-name "BucketName")))
149 (define-specific-error (no-such-key "NoSuchKey") ()
152 (define-specific-error (access-denied "AccessDenied") () ())
154 (define-specific-error (malformed-xml "MalformedXML") () ())
156 (define-condition redirect-error
() ())
158 (define-specific-error (permanent-redirect "PermanentRedirect") (redirect-error)
159 ((endpoint "Endpoint")))
161 (define-specific-error (temporary-redirect "TemporaryRedirect") (redirect-error)
162 ((endpoint "Endpoint")))
164 (define-specific-error (signature-mismatch "SignatureDoesNotMatch") ()
165 ((string-to-sign "StringToSign"))
166 (:report
(lambda (condition stream
)
167 (report-request-error condition stream
)
168 (format stream
"You signed: ~S~%Amazon signed: ~S"
169 (signed-string (request-error-request condition
))
170 (request-error-string-to-sign condition
)))))
172 (define-specific-error (precondition-failed "PreconditionFailed") ()
173 ((condition "Condition")))
177 (define-condition linked
()
181 (:report
(lambda (condition stream
)
182 (report-request-error condition stream
)
183 (format stream
"~&For more information, see:~% ~A"
184 (linked-url condition
)))))
187 (define-condition bucket-restrictions
(linked)
190 :url
"http://docs.amazonwebservices.com/AmazonS3/2006-03-01/BucketRestrictions.html"))
192 (define-specific-error (invalid-bucket-name "InvalidBucketName")
193 (bucket-restrictions)
196 (define-specific-error (bucket-exists "BucketAlreadyExists")
197 (bucket-restrictions)
200 (define-specific-error (too-many-buckets "TooManyBuckets")
201 (bucket-restrictions)
205 (define-specific-error (ambiguous-grant "AmbiguousGrantByEmailAddress") (linked)
208 :url
"http://docs.amazonwebservices.com/AmazonS3/2006-03-01/S3_ACLs.html"))
210 (define-specific-error (bucket-not-empty "BucketNotEmpty") (linked)
213 :url
"http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTBucketDELETE.html"))
215 (define-specific-error (invalid-logging-target "InvalidTargetBucketForLogging")
218 (define-specific-error (key-too-long "KeyTooLong") (linked)
221 :url
"http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingKeys.html"))
223 (define-specific-error (request-time-skewed "RequestTimeTooSkewed") (linked)
226 :url
"http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAuthentication.html"))
228 (define-specific-error (operation-aborted "OperationAborted") () ())