I'm a big fan of Amazon Web Services (AWS). I'm using them in almost all of my projects. One of their most popular services is Simple Storage Service (S3). It is a storage for binary objects (files) with unique names, accessible through HTTP or RESTful API.
Using S3 is very simple. You create a "bucket" with a unique name, upload your "object" into the bucket through their web interface or through RESTful API, and then download it again (either through HTTP or the API.)
Amazon ships the Java SDK that wraps their RESTful API. However, this SDK is not object-oriented at all. It is purely imperative and procedural — it just mirrors the API.
For example, in order to download an existing object
test-1, you have to do something like this:
As always, procedural programming has its inevitable disadvantages. To overcome them all, I designed jcabi-s3, which is a small object-oriented adapter for Amazon SDK. This is how the same object-reading task can be accomplished with jcabi-s3:
Why is this approach better? Well, there are a number of obvious advantages.
S3 Object is an Object in Java
S3 object get its representative in Java. It is not
a collection of procedures to be called in order to
get its properties (as with AWS SDK). Rather, it is a Java object
with certain behaviors. I called them "ockets" (similar to "buckets"),
in order to avoid clashes with
an interface, that exposes the behavior of a real AWS S3 object:
read, write, check existence. There is also a convenient decorator
Ocket.Text that simplifies working with binary objects:
Now, you can pass an object to another class, instead of giving it your AWS credentials, bucket name, and object name. You simply pass a Java object, which encapsulates all AWS interaction details.
Extendability Through Decoration
For example, you want your code to retry S3 object read operations
a few times before giving up and
(by the way, this is a very good practice when working with web services).
So, you want all your S3 reading operations to be redone a few times
if first attempts fail.
You define a new decorator class, say,
which encapsulates an original
Now, everywhere where
Ocket is expected you send
an instance of
RetryingOcket that wraps your original object:
foo.process() won't see a difference, since
it is the same
Ocket interface it is expecting.
Again, due to the fact that all entities in
jcabi-s3 are interfaces, they are
very easy to mock. For example, your class expects an
S3 object, reads its data and calculates the MD5
hash (I'm using
Here is how simple a unit test will look (try to create a unit test for a class using AWS SDK and you will see the difference):
As always, your comments and criticism are welcome as GitHub issues.