RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Creating a Cloud Service for Amazon Web Services : Page 2

Learn how to create services that can be run in a cloud environment. Running in the cloud allows you to start quickly, with minimal expenses and lots of flexibility.


Creating a service that can run in the Amazon cloud

Most of the services offered by our sample organization, OpenGov, focus on allowing residents of a city to communicate with various local city departments. One of the applications offered by OpenGov provides different kinds of information regarding the city, for example, the opening hours of shops, garbage collection times, festival schedules. OpenGov wants to extend this platform so that residents can share information. In their first iteration, they decide that they'll offer a service where residents can share images and comment on those images. Besides storing the image, OpenGov also wants to provide a simple query function, where based on location a set of images and comments can be retrieved. Since they expect much usage of this service, and it's one of the policies defined by the OpenGov organization, this service will be created to run in the cloud. Figure 1 shows a high-level overview of this service, which we'll call ImageCommentService.

Cloud Development: Creating Cloud Services
Figure 1. The components of the ImageCommentService

Figure 1 shows that we provide a REST layer which the users of our service can use to add their images. Through a data layer, this information is stored in a datastore. You'll see how to map these various components to the components provided by Amazon. We start with the data layer and see how we can implement that layer with Amazon Web Services.

The data layer

With this service, the user can store an image together with some metadata. This means that our data layer needs to be able to store binary data, the image, and a set of metadata information. Listing 1 shows a simple POJO that captures the information that we want to store.

Listing 1. The model for the ImageCommentService

public class ImageComment {     private InputStream stream;     private String comments;     private String location;     private String timestamp;     private Map<String, String> userdata; }

Listing 1 shows binary data (the InputStream), a number of Strings, and a map containing an arbitrary amount of custom user information. What options does Amazon offer for us to store this image and its metadata? In Figure 2, you can see the complete set of AWS products offered by Amazon. One of these products should be able to fill our requirements.

Cloud Development: Creating Cloud Services
Click here for larger image

Figure 2. The services offered by Amazon Web Services

The interesting services here are the ones in the Database and Storage lists. From these lists, we could use the following products to store data in the cloud:

  • Amazon Simple Storage Service (S3) -- With S3, Amazon offers a simple REST interface you can use to store objects in the cloud. These can be objects anywhere from 1 byte to 5 terabytes. Each object can be retrieved using a unique, developer-assigned key.
  • Amazon SimpleDB -- With Amazon SimpleDB, you can easily store and query information in a key/value-based manner. All of the stored values are indexed for easy searching.
  • Amazon Relation Database Service (RDS) -- Amazon RDS provides a relational database you can use in the cloud. You can choose to use a MySQL or an Oracle-based database.

If we look back at the model from listing 1, you can see that we have two different types of data to store. On the one hand, we've got the binary data representing the image and we've got a set of key/value pairs. For this example, we'll store the image in S3, since it's a blob of unstructured data, and we'll store the key/values pair in Amazon SimpleDB, where we'll also store the reference to the object stored in S3. We store this object using the storeImageComment operation of the AmaxonImageCommentRepository. The first part of the storeImageComment method is shown in listing 1 and the second part is shown in listing 3. In Listing 2, we show you the basics of how you can store objects using S3.

Listing 2. Using Amazon S3 to store an object (AmazonImageCommentRepository)

@PostConstruct public void init() {   s3 = new AmazonS3Client(new BasicAWSCredentials(sharedKey,secret)); #1   bucket = s3.createBucket(SOAGOV_BUCKET);  #2   ... }   public void storeImageComment(ImageComment comment) {   String key = KEY_PREFIX + UUID.randomUUID();          #3   s3.putObject(bucket.getName(), key ,comment.getStream(), null); #4   ... }

#1 Creates an S3 client
#2 Creates a bucket
#3 Generates a unique key
#4 Stores the data

To store an object, we use the Amazon-provided Java client. This client handles all the low-level REST calls. All we have to do is specify the shared key and the secret to use with the call (#1). If you've registered with AWS, you can get this information from your account page. Once you've set up a client, the next step is to make sure we've got a bucket. A bucket is a container in which we store our objects. The code at #2 will create a new bucket if one with the specified name doesn't already exist. All we need to store the object is the inputstream, a unique key (#3), and the name of the bucket in which to store the data. If we call s3.putObject with these arguments, the object will be stored in the Amazon S3 cloud.

Now that we've stored the image, we also need to store the metadata of the object. Listing 3 shows how you can use Amazon SimpleDB to store this information.

Listing 3. Using Amazon SimpleDB to store the metadata

@PostConstruct public void init() {  ...  sdb = new AmazonSimpleDBClient(                 #1    new BasicAWSCredentials(sharedKey,secret));   #1  sdb.createDomain(new CreateDomainRequest(SOAGOV_DOMAIN));  #2 }   public void storeImageComment(ImageComment comment) {   ...   List<ReplaceableAttribute> attributes = new      #3     ArrayList<ReplaceableAttribute>();             #3   attributes.add(new ReplaceableAttribute(     TIMESTAMP, Long.toString(System.currentTimeMillis()), true));   attributes.add(new ReplaceableAttribute(     COMMENTS, comment.getComments(), true));   attributes.add(new ReplaceableAttribute(     LOCATION, comment.getLocation(), true));               Map<String,String> otherData = comment.getUserdata(); #4   for (String userelement : otherData.keySet()) {        #4     attributes.add(new ReplaceableAttribute(            #4       userelement, otherData.get(userelement), true));   #4   }                                    sdb.putAttributes(new PutAttributesRequest(   #5     SOAGOV_DOMAIN, key, attributes));            #5 }

#1 Creates a client for simpleDB
#2 Creates domain
#3 Attributes to store
#4 Adds userdata
#5 Stores in the database

In listing 3, you can see how we can use the AmazonSimpleDBClient to store the non-binary information from our ImageComment class. We first get a client, just as we did for S3 (#1). This time, we also need to create the location where our information needs to be stored. For simpleDB, this is called a domain, which we create in #2. We can then use this client to store the rest of the data. We do this by creating a list of attributes (#3), which are simple name/value pairs. We also add the user-specified data (#4) to this attribute list and, finally, use the putAttributes method to store the information. One thing to notice is that we use the same key in this operation (#5) as we did for the call to S3 in listing 2. This way, we can easily correlate the information from S3 with the information we stored in simpleDB, since both keys are the same.

We also mentioned that we would like to support searching for ImageComments based on location. We won't show you the complete code for this, but we'll just show you how to query simpleDB and retrieve information from S3. Querying simpleDB can be done in a way similar to SQL. For our scenario, we use the following statements for our search function:

String selectExpression = "select * from '"+ SOAGOV_DOMAIN
  +"' where location like '%" + location + "%'";
SelectRequest selectRequest = new SelectRequest(selectExpression);
List<Item> queryResut = sdb.select(selectRequest).getItems();

With these couple of statements, we create an SQL-like query, and use the AmazonSimpleDBClient to execute he query. This will return a set of Item objects, where each item contains a name that contains the value of our key, and all the attributes we stored. Because the image we stored in S3 was stored with the same name as the metadata in SimpleDB, we can also use this name to retrieve the image data from S3.

InputStream data = s3.getObject
            (bucket.getName(), key).getObjectContent();

With this information, we can create list of ImageComment objects that we return as search results. Note that in this simple cloud example we haven't defined an internal service layer. The remoting layer directly uses the data layer to store and query data.

The remoting layer

For the REST remoting layer, we can use the same setup as we did for the other REST-based services. We create a Spring bean and use JAX-RS annotations to define which method should be invoked when a specific resource location is requested with a specific HTTP verb. Listing 4 shows the remoting layer for this service.

Listing 4. The REST remoting layer

@Service @Path("/opengov/govdata/") public class GovDataCloudService {     @Resource(name="ImageCommentRepository")   private ImageCommentRepository repository;      @PUT   public Response addImageComment(String jsonData) {     ImageComment imagecomment = jsonToToImageComment(jsonData); #1     repository.storeImageComment(imagecomment);                 #1     return Response.ok().build();   }     @GET   @Produces("application/cloudservice.imagecomments+json")   public Response searchImageComments(@QueryParam("loc")  String query)                        throws IOException {     JSONObject responseData = new JSONObject();          List<ImageComment> comments =                                #2              repository.findImageCommentsForLocation(query);     #2     List<JSONObject> foundElements = new ArrayList<JSONObject>();     for (ImageComment imageComment : comments) {       foundElements.add(imageCommentToJson(imageComment));        }          return Response.ok().entity(responseData          .element("imagecomments", foundElements).toString(3,1)).build();   }

#1 Converts and stores the JSON request
#2 Finds all ImageComment objects

In this listing, we don't do anything that is specific to Amazon or for running in the cloud. It's just a basic JAX-RS-based service that uses the repository (#1) to store incoming data and to find (#2) ImageComments based on a location.

So far, we've seen how the data layer and the REST layer are implemented. The final step we need to do is create the Spring application context that glues everything together. Listing 5 shows the Spring configuration file used in this example.

Listing 5. Spring configuration for the Amazon cloud-based service

  <import resource="classpath:META-INF/cxf/cxf.xml" />   <import resource="classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml" />   <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />     <jaxrs:server id="customerService" address="/cloud">   <jaxrs:serviceBeans>     <ref bean="cloudServiceBean" />   </jaxrs:serviceBeans> </jaxrs:server>   <bean id="cloudServiceBean" class="...services.rest.GovDataCloudService" />    <bean id="ImageCommentRepository"     class="...data.impl.AmazonImageCommentRepository" >     <property name="sharedKey" value="sharedkeyfromyouraccount"/>     <property name="secret" value="secretfromyouraccount"/>   </bean>

Listing 5 shows you see a basic JAX-RS configuration. The main difference is that in this instance we don't specify the full address for our service but just the context "/cloud". The reason is that we'll be running this service in the container provided to us by Amazon, so the hostname and port is defined for us by this container.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date