                      (into {}))]
    (get q-attrs queue-arn-attribute)))
 
+
+(defn set-aws-client-attr!
+  "Conditionally configure the AWS client instance with by calling the given
+  method with the given args."
+  [client-obj method args]
+  (when-not (nil? (first args))
+    (debug "\tConfig:" method "=" args)
+    (Reflector/invokeInstanceMethod
+     client-obj (name method) (object-array args))))
+
+(defn configure-aws-client
+  "Configure an AWS service client with values passed pair-wise to the methods
+  they are passed with. This function allows for the configuration of an AWS
+  service object after instantiation by passing it first an AWS service client
+  instance, and then a series of keywords and configuration values, where the
+  keywords are spelled the same as the client object's given setter method
+  and the configuration values are the results of having called a configuration
+  function that will pull a value from the environment or the default, in the
+  event of an undefined environment value.
+
+  Example usage:
+  ```
+  (configure-aws-client
+    (new AmazonSQSClient)
+    :setEndpoint (sqs-endpoint)
+    :setRegion (sqs-region)
+    :setTimeOffset (sqs-time-offset)))
+  ```
+
+  With the understanding, of course, that the configuration functions in the
+  example all have been created.
+
+  Note that since AWS service clients will set service defaults on their own,
+  it is best to define the default configuration value as nil, so as to avoid
+  unexpected client behaviour."
+  [client-obj & args]
+  (debug "Configuring client" client-obj)
+  (doseq [[config-key config-val] (partition 2 args)]
+    (set-aws-client-attr! client-obj config-key [config-val]))
+  client-obj)
+
+(defmulti create-aws-client
+  "Create an AWS service client, conditionally setting any configured values.
+
+  Note that if the configuration calls (e.g., `(sqs-endpoint)`) return `nil`,
+  the configuration operation doesn't actually take place."
+  identity)
+
+(defmethod create-aws-client :sqs
+  [^Keyword type]
+  (configure-aws-client
+    (new AmazonSQSClient)
+    :setEndpoint (sqs-endpoint)))
+
+(defmethod create-aws-client :sns
+  [^Keyword type]
+  (configure-aws-client
+    (new AmazonSNSClient)
+    :setEndpoint (sns-endpoint)))
+
 (defn- create-async-handler
   "Creates a thread that will asynchronously pull messages off the queue, pass them to the handler,
   and process the response. Throwables raised while reading the queue are caught to avoid exiting

(def get-queue-url
  "Memoized function that returns the queue url for the given name."
  (memoize -get-queue-url))

(defn set-aws-client-attr!
  "Conditionally configure the AWS client instance with by calling the given
  method with the given args."
  [client-obj method args]
  (when-not (nil? (first args))
    (debug "\tConfig:" method "=" args)
    (Reflector/invokeInstanceMethod
     client-obj (name method) (object-array args))))

(defn configure-aws-client
  "Configure an AWS service client with values passed pair-wise to the methods
  they are passed with. This function allows for the configuration of an AWS
  service object after instantiation by passing it first an AWS service client
  instance, and then a series of keywords and configuration values, where the
  keywords are spelled the same as the client object's given setter method
  and the configuration values are the results of having called a configuration
  function that will pull a value from the environment or the default, in the
  event of an undefined environment value.

  Example usage:
  ```
  (configure-aws-client
    (new AmazonSQSClient)
    :setEndpoint (sqs-endpoint)
    :setRegion (sqs-region)
    :setTimeOffset (sqs-time-offset)))
  ```

  With the understanding, of course, that the configuration functions in the
  example all have been created.

  Note that since AWS service clients will set service defaults on their own,
  it is best to define the default configuration value as nil, so as to avoid
  unexpected client behaviour."
  [client-obj & args]
  (debug "Configuring client" client-obj)
  (doseq [[config-key config-val] (partition 2 args)]
    (set-aws-client-attr! client-obj config-key [config-val]))
  client-obj)

(defmulti create-aws-client
  "Create an AWS service client, conditionally setting any configured values.

  Note that if the configuration calls (e.g., `(sqs-endpoint)`) return `nil`,
  the configuration operation doesn't actually take place."
  identity)

(defmethod create-aws-client :sqs
  [^Keyword type]
  (configure-aws-client
    (new AmazonSQSClient)
    :setEndpoint (sqs-endpoint)))

(defmethod create-aws-client :sns
  [^Keyword type]
  (configure-aws-client
    (new AmazonSNSClient)
    :setEndpoint (sns-endpoint)))

(defrecord SQSQueueBroker
  ;; A record containing fields related to accessing SNS/SQS exchanges and queues.

                      ;; Tell SQS how long to wait before returning with no data (long polling).
                      (.setWaitTimeSeconds (Integer. (queue-polling-timeout))))]
    (a/thread
      (loop [sqs-client-atom (atom sqs-client)]
        (try
          (let [rec-result (.receiveMessage @sqs-client-atom rec-req)]
            (when-let [msg (first (.getMessages rec-result))]
              (let [msg-body (.getBody msg)
                    msg-content (json/decode msg-body true)]
                (try
                  (handler msg-content)
                  (.deleteMessage @sqs-client-atom queue-url (.getReceiptHandle msg))
                  (catch Throwable e
                    (error e "Message processing failed for message" (pr-str msg) "on queue" queue-name))))))
          ;; Catching this so the next catch block won't - this allows us to exit the thread after a test
