From 39437392507d4591c82c6ff226e3e6bcdef4e0f7 Mon Sep 17 00:00:00 2001 From: fulghum Date: Mon, 23 Sep 2013 14:46:56 -0700 Subject: [PATCH] Updates to the AWS Meme Generator application for AWS re:Invent 2013 --- README.txt | 30 +-- projects/MemeCommon/.classpath | 1 + .../MemeCommon/src/AwsCredentials.properties | 2 +- projects/MemeCommon/src/META-INF/MANIFEST.MF | 3 - .../src/com/amazonaws/memes/AWSResources.java | 161 ++++++------ .../{MemeCreationJob.java => ImageMacro.java} | 62 ++--- .../src/com/amazonaws/memes/MemeStorage.java | 81 ------ .../com/amazonaws/memes/S3ImageStorage.java | 68 ----- projects/MemeWorker/.classpath | 3 +- projects/MemeWorker/.gitignore | 1 + .../MemeWorker/src/AwsCredentials.properties | 4 - .../src/com/amazonaws/memes/ImageOverlay.java | 21 +- .../src/com/amazonaws/memes/MemeWorker.java | 244 +++++++++--------- projects/Memes/.classpath | 5 + .../Memes/WebContent/WEB-INF/lib/dummy.txt | 1 - projects/Memes/WebContent/createMeme.jsp | 153 +++++------ projects/Memes/WebContent/index.jsp | 134 +++++----- projects/Memes/WebContent/memes.jsp | 97 +++---- projects/Memes/src/AwsCredentials.properties | 4 - .../src/com/amazonaws/memes/MemeUtils.java | 88 +++++++ 20 files changed, 511 insertions(+), 652 deletions(-) delete mode 100644 projects/MemeCommon/src/META-INF/MANIFEST.MF rename projects/MemeCommon/src/com/amazonaws/memes/{MemeCreationJob.java => ImageMacro.java} (61%) delete mode 100644 projects/MemeCommon/src/com/amazonaws/memes/MemeStorage.java delete mode 100644 projects/MemeCommon/src/com/amazonaws/memes/S3ImageStorage.java create mode 100644 projects/MemeWorker/.gitignore delete mode 100644 projects/MemeWorker/src/AwsCredentials.properties delete mode 100644 projects/Memes/WebContent/WEB-INF/lib/dummy.txt delete mode 100644 projects/Memes/src/AwsCredentials.properties create mode 100644 projects/Memes/src/com/amazonaws/memes/MemeUtils.java diff --git a/README.txt b/README.txt index 069bb36..81b78af 100644 --- a/README.txt +++ b/README.txt @@ -1,5 +1,5 @@ /* - * Copyright 2012 Amazon Technologies, Inc. + * Copyright 2013 Amazon Technologies, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ Meme Generator Sample Application ================================= - This sample application demonstrates using S3, DynamoDB, and SQS to run an image processing application with a web front-end on Elastic Beanstalk and a back-end running on EC2. For a demonstration of the application and a discussion of its architecture, please watch the following presentation. + This sample application demonstrates using Amazon S3, Amazon DynamoDB, and Amazon SQS to run an image processing application with a web front-end on AWS Elastic Beanstalk and a back-end running on Amazon EC2. For a demonstration of the application and a discussion of its architecture, please watch the following presentation. http://www.youtube.com/watch?v=YeRNErD81VA&feature=youtu.be @@ -25,30 +25,24 @@ Instruction for setup: 1) Download Eclipse for J2EE developers - 2) Download the AWS Toolkit for Eclipse from http://aws.amazon.com/eclipse - 3) Install a tomcat runtime environment for Eclipse. The simplest way is to choose Window > Preferences > Server > Runtime Environments. Then add a new Apache Tomcat 7.0 runtime environment, and use the "Download and Install" button to download and install a new version of Apache Tomcat. + 2) Install the AWS Toolkit for Eclipse from http://aws.amazon.com/eclipse + 3) Install a Tomcat runtime environment for Eclipse. The simplest way is to choose Window > Preferences > Server > Runtime Environments. Then add a new Apache Tomcat 7.0 runtime environment, and use the "Download and Install" button to download and install a new version of Apache Tomcat. 4) Import the 3 projects contained in this sample application into your eclipse workspace. Chose File > Import > Existing projects into workspace. Navigate to the directory containing this file and select the "projects" directory. - 5) You should now see the Memes, MemeCommon, and MemeWorker projects in your workspace. - 6) Add the AWS SDK for Java classpath container to each of the three projects. Right-click on each project and select Build Path > Configure Build Path... Then, in the "Libraries" tab, select "Add Library" and choose the AWS SDK for Java library. The latest SDK will be downloaded at this point, if you don't have one installed already. The code should all compile at this point. If not, clean all three projects with Project > Clean. - 7) For the Memes project, which runs in Tomcat, you will need to add the SDK jar and third-party jars into WebContent/WEB-INF/lib. The simplest way to do this is to drag and drop from the file explorer. - 7a) Find the directory where the AWS SDK for Java was downloaded. This defaults to ${home}/aws-java-sdk, but can be configured via preferences. We'll call this directory $SDK. - 7b) Drag the $SDK/lib/aws-java-sdk-X.Y.Z.jar file into Memes/WebContent/WEB-INF/lib in Eclipse's file explorer. Eclipse will ask if you want to link or copy the files. Copying is more dependable. - 7c) Repeat this process for every jar file in $SDK/third-party/*. You can leave out the aspectj, spring, freemarker, and java-mail libraries, since they aren't used by the sample. - 7d) Yes, the above process is very tedious. But we don't want to check an SDK (and all required third-party libs) into source control. - 8) Locate the AWSCredentials.properties file for each project, and fill in your access key and secret key - 9) In the MemeCommon project, edit src/com/amazonaws/memes/AWSResources.java to configure the name of the S3 bucket to use. The application uses the us-west-1 region by default. To use another region, edit the service endpoint constants in this file. - 10) To create all the AWS resources required by the application, run the above file as a Java program (right-click, Run As... > Java application) + 5) You should now see the Memes, MemeCommon, and MemeWorker projects in your workspace. If the code isn't compiling, make sure you've installed a Tomcat 7.0 runtime as described in step 3. If the code still doesn't compile, you can try cleaning all three projects with Project > Clean. + 6) Locate the AWSCredentials.properties file in the MemeCommon project, and fill in your AWS security credentials. + 7) In the MemeCommon project, edit src/com/amazonaws/memes/AWSResources.java to configure the name of the S3 bucket to use. The application uses the us-west-1 region by default. To use another region, edit the service endpoint constants in this file. + 8) To create all the AWS resources required by the application, run the above file as a Java program (right-click, Run As... > Java application) Deploying the Web application: - The application is now ready to deploy to elastic beanstalk (or to a local tomcat server). + The application is now ready to deploy to AWS Elastic Beanstalk (or to a local tomcat server). - To deploy the web application to elastic beanstalk, right-click on the Memes project in the package explorer, then select Run As... > Run on server. In the deployment wizard, you can create a new elastic beanstalk environment to deploy the application onto if you haven't created one yet. + To deploy the web application to AWS Elastic Beanstalk, right-click on the Memes project in the package explorer, then select Run As... > Run on server. In the deployment wizard, you can create a new AWS Elastic Beanstalk environment to deploy the application into if you haven't created one yet. Running the image processing worker: - The other half of the application is the back-end image processing worker, contained in the MemeWorker project. You can either run it locally or on EC2. + The other half of the application is the back-end image processing worker, contained in the MemeWorker project. You can either run it locally or on Amazon EC2. To run it locally, just right-click on the MemeWorker class and select Run As ... > Java application. - To run it on EC2, export the MemeWorker project as an executable jar using the File > Export menu. Then copy it to an EC2 host, and invoke the command "java -f ". \ No newline at end of file + To run it on Amazon EC2, export the MemeWorker project as an executable jar using the File > Export menu. Then copy it to an EC2 host, and invoke the command "java -f ". diff --git a/projects/MemeCommon/.classpath b/projects/MemeCommon/.classpath index ee46203..d221f1e 100644 --- a/projects/MemeCommon/.classpath +++ b/projects/MemeCommon/.classpath @@ -12,5 +12,6 @@ + diff --git a/projects/MemeCommon/src/AwsCredentials.properties b/projects/MemeCommon/src/AwsCredentials.properties index 5a595c9..43f23d0 100644 --- a/projects/MemeCommon/src/AwsCredentials.properties +++ b/projects/MemeCommon/src/AwsCredentials.properties @@ -1,4 +1,4 @@ #Insert your AWS Credentials from http://aws.amazon.com/security-credentials #Tue Sep 25 14:56:08 PDT 2012 -secretKey=CHANGEME accessKey=CHANGEME +secretKey=CHANGEME diff --git a/projects/MemeCommon/src/META-INF/MANIFEST.MF b/projects/MemeCommon/src/META-INF/MANIFEST.MF deleted file mode 100644 index 254272e..0000000 --- a/projects/MemeCommon/src/META-INF/MANIFEST.MF +++ /dev/null @@ -1,3 +0,0 @@ -Manifest-Version: 1.0 -Class-Path: - diff --git a/projects/MemeCommon/src/com/amazonaws/memes/AWSResources.java b/projects/MemeCommon/src/com/amazonaws/memes/AWSResources.java index ba60968..037c4ee 100644 --- a/projects/MemeCommon/src/com/amazonaws/memes/AWSResources.java +++ b/projects/MemeCommon/src/com/amazonaws/memes/AWSResources.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 Amazon Technologies, Inc. + * Copyright 2012-2013 Amazon Technologies, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,110 +14,97 @@ */ package com.amazonaws.memes; -import com.amazonaws.AmazonServiceException; +import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.auth.ClasspathPropertiesFileCredentialsProvider; -import com.amazonaws.services.dynamodb.AmazonDynamoDB; -import com.amazonaws.services.dynamodb.AmazonDynamoDBClient; -import com.amazonaws.services.dynamodb.model.CreateTableRequest; -import com.amazonaws.services.dynamodb.model.DescribeTableRequest; -import com.amazonaws.services.dynamodb.model.KeySchema; -import com.amazonaws.services.dynamodb.model.KeySchemaElement; -import com.amazonaws.services.dynamodb.model.ProvisionedThroughput; -import com.amazonaws.services.dynamodb.model.ScalarAttributeType; -import com.amazonaws.services.dynamodb.model.TableDescription; -import com.amazonaws.services.dynamodb.model.TableStatus; -import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.regions.Region; +import com.amazonaws.regions.Regions; +import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; +import com.amazonaws.services.dynamodbv2.model.AttributeDefinition; +import com.amazonaws.services.dynamodbv2.model.CreateTableRequest; +import com.amazonaws.services.dynamodbv2.model.KeySchemaElement; +import com.amazonaws.services.dynamodbv2.model.KeyType; +import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput; +import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType; +import com.amazonaws.services.dynamodbv2.util.Tables; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.CreateBucketRequest; -import com.amazonaws.services.sqs.AmazonSQS; import com.amazonaws.services.sqs.AmazonSQSClient; import com.amazonaws.services.sqs.model.CreateQueueRequest; /** - * Configuration for AWS resources. Run to set up your resources. - * - * @author zachmu + * Configuration for AWS resources. Run this class to create your resources for + * the Meme Generator sample application. */ public class AWSResources { - public static final String BUCKET_NAME = "CHANGEME"; + public static final String S3_BUCKET_NAME = "reinvent-meme-generator"; + public static final String SQS_QUEUE_NAME = "reinvent-memes"; + public static final String DYNAMODB_TABLE_NAME = "reinvent-memes"; + public static final String MACRO_PATH = "macros/"; - public static final String FINISHED_PATH = "memes/"; - public static final String SQS_QUEUE = "reinvent-memes"; - public static final String DYNAMO_TABLE_NAME = "reinvent-memes"; - - public static final String SQS_ENDPOINT = "sqs.us-west-1.amazonaws.com"; - public static final String DYNAMO_ENDPOINT = "dynamodb.us-west-1.amazonaws.com"; - - public static void main(String[] args) { - createResources(); - } - - private static void createResources() { - ClasspathPropertiesFileCredentialsProvider provider = new ClasspathPropertiesFileCredentialsProvider(); - - AmazonSQS sqs = new AmazonSQSClient(provider); - sqs.setEndpoint(SQS_ENDPOINT); - sqs.createQueue(new CreateQueueRequest().withQueueName(SQS_QUEUE)); - - AmazonS3 s3 = new AmazonS3Client(provider); - if ( !s3.doesBucketExist(BUCKET_NAME) ) { - s3.createBucket(new CreateBucketRequest(BUCKET_NAME)); - } - - AmazonDynamoDBClient dynamo = new AmazonDynamoDBClient(provider); - dynamo.setEndpoint(DYNAMO_ENDPOINT); + public static final String FINISHED_PATH = "memes/"; - if ( !doesTableExist(dynamo, DYNAMO_TABLE_NAME) ) { - dynamo.createTable(new CreateTableRequest() - .withTableName(DYNAMO_TABLE_NAME) - .withProvisionedThroughput( - new ProvisionedThroughput().withReadCapacityUnits(50l).withWriteCapacityUnits(50l)) - .withKeySchema( - new KeySchema().withHashKeyElement(new KeySchemaElement().withAttributeName("id") - .withAttributeType(ScalarAttributeType.S)))); - waitForTableToBecomeAvailable(dynamo, DYNAMO_TABLE_NAME); - } - } + /* + * The SDK provides several easy to use credentials providers. + * Here we're loading our AWS security credentials from a properties + * file on our classpath. + */ + public static final AWSCredentialsProvider CREDENTIALS_PROVIDER = + new ClasspathPropertiesFileCredentialsProvider(); - private static boolean doesTableExist(AmazonDynamoDB dynamo, String tableName) { - try { - TableDescription table = dynamo.describeTable(new DescribeTableRequest().withTableName(tableName)) - .getTable(); - return "ACTIVE".equals(table.getTableStatus()); - } catch ( AmazonServiceException ase ) { - if ( ase.getErrorCode().equals("ResourceNotFoundException") ) - return false; - throw ase; - } + /* + * This controls the AWS region used for created resources. You can easily + * deploy applications in any or all of the AWS regions around the world, + * allowing you to provide a lower latency and better experience for your + * customers. + */ + public static final Region REGION = Region.getRegion(Regions.US_WEST_1); + + /* + * We construct our clients to access AWS here, so that we can share them + * easily throughout our application. + */ + public static final AmazonS3Client S3 = new AmazonS3Client(CREDENTIALS_PROVIDER); + public static final AmazonSQSClient SQS = new AmazonSQSClient(CREDENTIALS_PROVIDER); + public static final AmazonDynamoDBClient DYNAMODB = new AmazonDynamoDBClient(CREDENTIALS_PROVIDER); + public static final DynamoDBMapper DYNAMODB_MAPPER = new DynamoDBMapper(DYNAMODB, CREDENTIALS_PROVIDER); + + + static { + /* + * Set any other client options that you need here. For example, if you + * connect to the internet through a proxy, then call setConfiguration + * and pass in a ClientConfiguration object with your proxy settings. + * + * Here we set our region, so that we can keep our data located in the + * same region. + */ + DYNAMODB.setRegion(REGION); + SQS.setRegion(REGION); } - private static void waitForTableToBecomeAvailable(AmazonDynamoDB dynamo, String tableName) { - System.out.println("Waiting for " + tableName + " to become ACTIVE..."); - long startTime = System.currentTimeMillis(); - long endTime = startTime + (10 * 60 * 1000); - while ( System.currentTimeMillis() < endTime ) { - try { - Thread.sleep(1000 * 20); - } catch ( Exception e ) { - } - try { - DescribeTableRequest request = new DescribeTableRequest().withTableName(tableName); - TableDescription table = dynamo.describeTable(request).getTable(); - if ( table == null ) - continue; + public static void main(String[] args) { + String queueUrl = SQS.createQueue(new CreateQueueRequest(SQS_QUEUE_NAME)).getQueueUrl(); + System.out.println("Using Amazon SQS Queue: " + queueUrl); - String tableStatus = table.getTableStatus(); - System.out.println(" - current state: " + tableStatus); - if ( tableStatus.equals(TableStatus.ACTIVE.toString()) ) - return; - } catch ( AmazonServiceException ase ) { - if ( ase.getErrorCode().equalsIgnoreCase("ResourceNotFoundException") == false ) - throw ase; - } + + if ( !S3.doesBucketExist(S3_BUCKET_NAME) ) { + S3.createBucket(new CreateBucketRequest(S3_BUCKET_NAME)); } + System.out.println("Using Amazon S3 Bucket: " + S3_BUCKET_NAME); + - throw new RuntimeException("Table " + tableName + " never went active"); + if ( !Tables.doesTableExist(DYNAMODB, DYNAMODB_TABLE_NAME) ) { + System.out.println("Creating new AWS DynamoDB Table..."); + DYNAMODB.createTable(new CreateTableRequest() + .withTableName(DYNAMODB_TABLE_NAME) + .withKeySchema(new KeySchemaElement("id", KeyType.HASH)) + .withAttributeDefinitions(new AttributeDefinition("id", ScalarAttributeType.S)) + .withProvisionedThroughput(new ProvisionedThroughput(50l, 50l))); + } + Tables.waitForTableToBecomeActive(DYNAMODB, DYNAMODB_TABLE_NAME); + System.out.println("Using AWS DynamoDB Table: " + DYNAMODB_TABLE_NAME); } } diff --git a/projects/MemeCommon/src/com/amazonaws/memes/MemeCreationJob.java b/projects/MemeCommon/src/com/amazonaws/memes/ImageMacro.java similarity index 61% rename from projects/MemeCommon/src/com/amazonaws/memes/MemeCreationJob.java rename to projects/MemeCommon/src/com/amazonaws/memes/ImageMacro.java index ecef53b..ac0d303 100644 --- a/projects/MemeCommon/src/com/amazonaws/memes/MemeCreationJob.java +++ b/projects/MemeCommon/src/com/amazonaws/memes/ImageMacro.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 Amazon Technologies, Inc. + * Copyright 2012-2013 Amazon Technologies, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,36 +16,34 @@ import java.util.Date; -import com.amazonaws.services.dynamodb.datamodeling.DynamoDBAttribute; -import com.amazonaws.services.dynamodb.datamodeling.DynamoDBAutoGeneratedKey; -import com.amazonaws.services.dynamodb.datamodeling.DynamoDBHashKey; -import com.amazonaws.services.dynamodb.datamodeling.DynamoDBTable; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAutoGeneratedKey; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; +import com.amazonaws.services.dynamodbv2.datamodeling.S3Link; /** - * Data model class for a meme creation job stored in Dynamo DB - * - * @author zachmu + * Data model class for an image macro stored in Amazon DynamoDB. */ @DynamoDBTable(tableName = "reinvent-memes") -public class MemeCreationJob { +public class ImageMacro { private String id; - private String imageKey; private String topCaption; private String bottomCaption; - private Date creationTime; - private Date updateTime; - private String finishedKey; private String status; private String createdBy; + private S3Link startingImageLink; + private S3Link finishedImageLink; + private Date creationTime; + private Date updateTime; - public static final String NEW_STATUS = "New"; + public static final String NEW_STATUS = "New"; public static final String WORKING_STATUS = "Working"; - public static final String DONE_STATUS = "Done"; - public static final String FAILED_STATUS = "Failed"; + public static final String DONE_STATUS = "Done"; + public static final String FAILED_STATUS = "Failed"; - public MemeCreationJob() { - status = "New"; + public ImageMacro() { + status = NEW_STATUS; } @DynamoDBHashKey @@ -54,20 +52,18 @@ public String getId() { return id; } - public void setId(String id) { + public void setId(String id) { this.id = id; } - @DynamoDBAttribute - public String getImageKey() { - return imageKey; + public S3Link getStartingImageLink() { + return startingImageLink; } - public void setImageKey(String imageKey) { - this.imageKey = imageKey; + public void setStartingImageLink(S3Link startingImageLink) { + this.startingImageLink = startingImageLink; } - @DynamoDBAttribute public String getTopCaption() { return topCaption; } @@ -76,7 +72,6 @@ public void setTopCaption(String topCaption) { this.topCaption = topCaption; } - @DynamoDBAttribute public String getBottomCaption() { return bottomCaption; } @@ -85,7 +80,6 @@ public void setBottomCaption(String bottomCaption) { this.bottomCaption = bottomCaption; } - @DynamoDBAttribute public Date getCreationTime() { return creationTime; } @@ -94,7 +88,6 @@ public void setCreationTime(Date creationTime) { this.creationTime = creationTime; } - @DynamoDBAttribute public Date getUpdateTime() { return updateTime; } @@ -103,16 +96,14 @@ public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } - @DynamoDBAttribute - public String getFinishedKey() { - return finishedKey; + public S3Link getFinishedImageLink() { + return finishedImageLink; } - public void setFinishedKey(String finishedKey) { - this.finishedKey = finishedKey; + public void setFinishedImageLink(S3Link finishedImageLink) { + this.finishedImageLink = finishedImageLink; } - @DynamoDBAttribute public String getStatus() { return status; } @@ -121,11 +112,10 @@ public void setStatus(String status) { this.status = status; } - @DynamoDBAttribute public String getCreatedBy() { return createdBy; } - + public void setCreatedBy(String createdBy) { this.createdBy = createdBy; } diff --git a/projects/MemeCommon/src/com/amazonaws/memes/MemeStorage.java b/projects/MemeCommon/src/com/amazonaws/memes/MemeStorage.java deleted file mode 100644 index 6a74679..0000000 --- a/projects/MemeCommon/src/com/amazonaws/memes/MemeStorage.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2012 Amazon Technologies, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://aws.amazon.com/apache2.0 - * - * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES - * OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and - * limitations under the License. - */ -package com.amazonaws.memes; - -import java.util.List; - -import com.amazonaws.auth.ClasspathPropertiesFileCredentialsProvider; -import com.amazonaws.services.dynamodb.AmazonDynamoDBClient; -import com.amazonaws.services.dynamodb.datamodeling.DynamoDBMapper; -import com.amazonaws.services.dynamodb.datamodeling.DynamoDBScanExpression; -import com.amazonaws.services.dynamodb.model.AttributeValue; -import com.amazonaws.services.dynamodb.model.ComparisonOperator; -import com.amazonaws.services.dynamodb.model.Condition; -import com.amazonaws.services.sqs.AmazonSQS; -import com.amazonaws.services.sqs.AmazonSQSClient; -import com.amazonaws.services.sqs.model.GetQueueUrlRequest; -import com.amazonaws.services.sqs.model.SendMessageRequest; - - -/** - * Helper class to manage meme creation jobs - */ -public class MemeStorage { - - private final AmazonSQS sqs; - private final DynamoDBMapper dynamoDBMapper; - - /** - * Singleton instance to conserve resources - */ - private static MemeStorage _instance; - - public static synchronized MemeStorage getInstance() { - if (_instance == null) - _instance = new MemeStorage(); - return _instance; - } - - public MemeStorage() { - ClasspathPropertiesFileCredentialsProvider provider = new ClasspathPropertiesFileCredentialsProvider(); - sqs = new AmazonSQSClient(provider); - sqs.setEndpoint(AWSResources.SQS_ENDPOINT); - AmazonDynamoDBClient dynamoDB = new AmazonDynamoDBClient(provider); - dynamoDB.setEndpoint(AWSResources.DYNAMO_ENDPOINT); - dynamoDBMapper = new DynamoDBMapper(dynamoDB); - } - - public void submitJob(MemeCreationJob job) { - dynamoDBMapper.save(job); - String queueUrl = sqs.getQueueUrl(new GetQueueUrlRequest().withQueueName(AWSResources.SQS_QUEUE)).getQueueUrl(); - sqs.sendMessage(new SendMessageRequest().withMessageBody(job.getId()).withQueueUrl(queueUrl)); - } - - public MemeCreationJob loadJob(String id) { - return dynamoDBMapper.load(MemeCreationJob.class, id); - } - - public void saveJob(MemeCreationJob job) { - dynamoDBMapper.save(job); - } - - public List getFinishedJobs() { - DynamoDBScanExpression scanExpression = new DynamoDBScanExpression(); - scanExpression.addFilterCondition("status", new Condition().withComparisonOperator(ComparisonOperator.EQ) - .withAttributeValueList(new AttributeValue().withS(MemeCreationJob.DONE_STATUS))); - return dynamoDBMapper.scan(MemeCreationJob.class, scanExpression); - } - -} diff --git a/projects/MemeCommon/src/com/amazonaws/memes/S3ImageStorage.java b/projects/MemeCommon/src/com/amazonaws/memes/S3ImageStorage.java deleted file mode 100644 index 24a06f8..0000000 --- a/projects/MemeCommon/src/com/amazonaws/memes/S3ImageStorage.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2012 Amazon Technologies, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://aws.amazon.com/apache2.0 - * - * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES - * OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and - * limitations under the License. - */ -package com.amazonaws.memes; - -import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.List; - -import javax.imageio.ImageIO; - -import com.amazonaws.auth.ClasspathPropertiesFileCredentialsProvider; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3Client; -import com.amazonaws.services.s3.model.CannedAccessControlList; -import com.amazonaws.services.s3.model.S3Object; -import com.amazonaws.services.s3.model.S3ObjectSummary; - -/** - * Stores and retrieves images in S3. - */ -public class S3ImageStorage { - - private static final AmazonS3 s3 = new AmazonS3Client(new ClasspathPropertiesFileCredentialsProvider()); - - public S3ImageStorage() { - } - - public BufferedImage loadBlankImage(String key) throws IOException { - S3Object object = s3.getObject(AWSResources.BUCKET_NAME, key); - return ImageIO.read(object.getObjectContent()); - } - - /** - * Stores the given finished image and returns the key it is stored with. - */ - public String storeFinishedImage(BufferedImage image, String key) throws IOException { - key = AWSResources.FINISHED_PATH + key + ".png"; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ImageIO.write(image, "png", baos); - baos.close(); - s3.putObject(AWSResources.BUCKET_NAME, key, new ByteArrayInputStream(baos.toByteArray()), null); - s3.setObjectAcl(AWSResources.BUCKET_NAME, key, CannedAccessControlList.PublicRead); - return key; - } - - public List getMacros() { - return s3.listObjects(AWSResources.BUCKET_NAME, AWSResources.MACRO_PATH).getObjectSummaries(); - } - - public List getFinishedMemes() { - return s3.listObjects(AWSResources.BUCKET_NAME, AWSResources.FINISHED_PATH).getObjectSummaries(); - } - -} diff --git a/projects/MemeWorker/.classpath b/projects/MemeWorker/.classpath index e68ad46..bc41a7c 100644 --- a/projects/MemeWorker/.classpath +++ b/projects/MemeWorker/.classpath @@ -1,7 +1,8 @@ - + + diff --git a/projects/MemeWorker/.gitignore b/projects/MemeWorker/.gitignore new file mode 100644 index 0000000..5e56e04 --- /dev/null +++ b/projects/MemeWorker/.gitignore @@ -0,0 +1 @@ +/bin diff --git a/projects/MemeWorker/src/AwsCredentials.properties b/projects/MemeWorker/src/AwsCredentials.properties deleted file mode 100644 index be82ba6..0000000 --- a/projects/MemeWorker/src/AwsCredentials.properties +++ /dev/null @@ -1,4 +0,0 @@ -#Insert your AWS Credentials from http://aws.amazon.com/security-credentials -#Tue Sep 25 10:22:14 PDT 2012 -secretKey=CHANGEME -accessKey=CHANGEME diff --git a/projects/MemeWorker/src/com/amazonaws/memes/ImageOverlay.java b/projects/MemeWorker/src/com/amazonaws/memes/ImageOverlay.java index 5944fa1..7b1ba13 100644 --- a/projects/MemeWorker/src/com/amazonaws/memes/ImageOverlay.java +++ b/projects/MemeWorker/src/com/amazonaws/memes/ImageOverlay.java @@ -1,6 +1,5 @@ /* - * Copyright 2012 Amazon Technologies, Inc. - + * Copyright 2012-2013 Amazon Technologies, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -65,7 +64,6 @@ public static void main(String[] args) throws InterruptedException { ImageIO.write(image, "png", new File(CAPTION_FILE)); } catch ( IOException e ) { - // TODO Auto-generated catch block e.printStackTrace(); } } @@ -75,9 +73,9 @@ public static void main(String[] args) throws InterruptedException { * bottom 20% of the image given. */ private static void drawStringCentered(Graphics g, String text, BufferedImage image, boolean top) throws InterruptedException { - if (text == null) - text = ""; - + if (text == null) + text = ""; + int height = 0; int fontSize = MAX_FONT_SIZE; int maxCaptionHeight = image.getHeight() / 5; @@ -92,15 +90,15 @@ private static void drawStringCentered(Graphics g, String text, BufferedImage im int left = 0; int right = text.length() - 1; while ( left < right ) { - + String substring = text.substring(left, right + 1); Rectangle2D stringBounds = g.getFontMetrics().getStringBounds(substring, g); while ( stringBounds.getWidth() > maxLineWidth ) { - if (Thread.currentThread().isInterrupted()) { - throw new InterruptedException(); - } + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException(); + } - // look for a space to break the line + // look for a space to break the line boolean spaceFound = false; for ( int i = right; i > left; i-- ) { if ( text.charAt(i) == ' ' ) { @@ -151,5 +149,4 @@ private static void drawStringCentered(Graphics g, String text, BufferedImage im y += g.getFontMetrics().getHeight(); } } - } diff --git a/projects/MemeWorker/src/com/amazonaws/memes/MemeWorker.java b/projects/MemeWorker/src/com/amazonaws/memes/MemeWorker.java index f7b6463..024a928 100644 --- a/projects/MemeWorker/src/com/amazonaws/memes/MemeWorker.java +++ b/projects/MemeWorker/src/com/amazonaws/memes/MemeWorker.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 Amazon Technologies, Inc. + * Copyright 2012-2013 Amazon Technologies, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,18 +14,23 @@ */ package com.amazonaws.memes; +import static com.amazonaws.memes.AWSResources.DYNAMODB_MAPPER; +import static com.amazonaws.memes.AWSResources.SQS; +import static com.amazonaws.memes.AWSResources.SQS_QUEUE_NAME; + import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Date; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import com.amazonaws.auth.ClasspathPropertiesFileCredentialsProvider; -import com.amazonaws.services.sqs.AmazonSQS; -import com.amazonaws.services.sqs.AmazonSQSClient; +import javax.imageio.ImageIO; + +import com.amazonaws.services.s3.model.CannedAccessControlList; import com.amazonaws.services.sqs.model.DeleteMessageRequest; import com.amazonaws.services.sqs.model.GetQueueUrlRequest; import com.amazonaws.services.sqs.model.Message; @@ -33,127 +38,116 @@ import com.amazonaws.services.sqs.model.ReceiveMessageResult; /** - * The meme worker's job is to take work from SQS, look up the details in - * Dynamo, then store the results back into S3. - * - * @author zachmu + * The meme worker's job is to take work from Amazon SQS, look up the details in + * Dynamo, then store the results back into Amazon S3. */ public class MemeWorker extends Thread { - private final S3ImageStorage s3ImageStore; - private final AmazonSQS sqs; - private final MemeStorage memeStorage; - private final ScheduledExecutorService executorService; - - public MemeWorker() { - ClasspathPropertiesFileCredentialsProvider provider = new ClasspathPropertiesFileCredentialsProvider(); - s3ImageStore = new S3ImageStorage(); - sqs = new AmazonSQSClient(provider); - sqs.setEndpoint(AWSResources.SQS_ENDPOINT); - memeStorage = new MemeStorage(); - executorService = new ScheduledThreadPoolExecutor(10); - } - - public static void main(String[] args) { - MemeWorker memeWorker = new MemeWorker(); - memeWorker.start(); - try { - memeWorker.join(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - @Override - public void run() { - String queueUrl = sqs.getQueueUrl( - new GetQueueUrlRequest().withQueueName(AWSResources.SQS_QUEUE)) - .getQueueUrl(); - - while (true) { - try { - ReceiveMessageResult receiveMessage = sqs - .receiveMessage(new ReceiveMessageRequest() - .withMaxNumberOfMessages(1).withQueueUrl( - queueUrl)); - for (Message msg : receiveMessage.getMessages()) { - processMessage(msg, queueUrl); - } - sleep(500); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return; - } catch (Exception e) { - // ignore and retry - } - } - } - - /** - * Processes the message given - */ - private void processMessage(final Message msg, final String queueUrl) - throws IOException { - - Runnable processRunnable = new Runnable() { - - @Override - public void run() { - String id = msg.getBody(); - MemeCreationJob job = memeStorage.loadJob(id); - - try { - if (job != null) { - job.setStatus(MemeCreationJob.WORKING_STATUS); - memeStorage.saveJob(job); - - BufferedImage image = s3ImageStore.loadBlankImage(job - .getImageKey()); - BufferedImage overlay = overlayImage(job, image); - String finishedImageKey = s3ImageStore.storeFinishedImage( - overlay, job.getId()); - job.setFinishedKey(finishedImageKey); - job.setStatus(MemeCreationJob.DONE_STATUS); - job.setUpdateTime(new Date()); - memeStorage.saveJob(job); - } - } catch (Exception e) { - e.printStackTrace(); - // Assume this job didn't work as expected - try { - job.setStatus(MemeCreationJob.FAILED_STATUS); - memeStorage.saveJob(job); - } catch (Exception e2) { - // oh well. - } - } - - sqs.deleteMessage(new DeleteMessageRequest().withQueueUrl(queueUrl) - .withReceiptHandle(msg.getReceiptHandle())); - } - }; - - executorService.schedule(processRunnable, 0, TimeUnit.SECONDS); - } - - /** - * Performs the buffered image overlay process with a timeout - */ - private BufferedImage overlayImage(MemeCreationJob job, BufferedImage image) - throws IOException, InterruptedException { - - // A timer to interrupt us after a timeout - final Thread thread = Thread.currentThread(); - Timer timer = new Timer(); - timer.schedule(new TimerTask() { - @Override - public void run() { - thread.interrupt(); - } - }, 10000); - - BufferedImage overlay = ImageOverlay.overlay(image, - job.getTopCaption(), job.getBottomCaption()); - return overlay; - } + private final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(10); + + public static void main(String[] args) throws InterruptedException { + new MemeWorker().start(); + } + + @Override + public void run() { + System.out.println("MemeWorker listening for work"); + String queueUrl = SQS.getQueueUrl(new GetQueueUrlRequest(SQS_QUEUE_NAME)).getQueueUrl(); + + while (true) { + try { + ReceiveMessageResult result = SQS.receiveMessage( + new ReceiveMessageRequest(queueUrl).withMaxNumberOfMessages(1)); + for (Message msg : result.getMessages()) { + executorService.submit(new MessageProcessor(queueUrl, msg)); + } + sleep(1000); + } catch (InterruptedException e) { + Thread.interrupted(); + throw new RuntimeException("Worker interrupted"); + } catch (Exception e) { + // ignore and retry + } + } + } + + private final class MessageProcessor implements Runnable { + private final String queueUrl; + private final Message msg; + + private MessageProcessor(String queueUrl, Message msg) { + this.queueUrl = queueUrl; + this.msg = msg; + } + + @Override + public void run() { + String id = msg.getBody(); + final ImageMacro imageMacro = DYNAMODB_MAPPER.load(ImageMacro.class, id); + + try { + if (imageMacro != null) { + imageMacro.setStatus(ImageMacro.WORKING_STATUS); + DYNAMODB_MAPPER.save(imageMacro); + + // Process the image + ByteArrayOutputStream output = new ByteArrayOutputStream(); + imageMacro.getStartingImageLink().downloadTo(output); + BufferedImage sourceImage = ImageIO.read(new ByteArrayInputStream(output.toByteArray())); + BufferedImage finishedImage = overlayImage(imageMacro, sourceImage); + + // Push the new image to S3 + imageMacro.getFinishedImageLink().uploadFrom(readImageIntoBuffer(finishedImage)); + imageMacro.getFinishedImageLink().setAcl(CannedAccessControlList.PublicRead); + + imageMacro.setStatus(ImageMacro.DONE_STATUS); + imageMacro.setUpdateTime(new Date()); + DYNAMODB_MAPPER.save(imageMacro); + } + } catch (Exception e) { + e.printStackTrace(); + try { + // Assume this job didn't work as expected + imageMacro.setStatus(ImageMacro.FAILED_STATUS); + DYNAMODB_MAPPER.save(imageMacro); + } catch (Exception e2) { + e2.printStackTrace(); + } + } + + SQS.deleteMessage(new DeleteMessageRequest(queueUrl, msg.getReceiptHandle())); + } + } + + /** + * Overlays the specified image with the captions from the MemeCreationJob + * and returns a new image. If the processing takes too long, this method + * will exit with an InterruptedException. + */ + private BufferedImage overlayImage(ImageMacro imageMacro, BufferedImage image) + throws IOException, InterruptedException { + + // A timer to interrupt us after a timeout + final Thread thread = Thread.currentThread(); + Timer timer = new Timer(); + timer.schedule(new TimerTask() { + @Override + public void run() { + thread.interrupt(); + } + }, 10000); + + return ImageOverlay.overlay( + image, imageMacro.getTopCaption(), imageMacro.getBottomCaption()); + } + + private byte[] readImageIntoBuffer(BufferedImage image) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + ImageIO.write(image, "png", baos); + return baos.toByteArray(); + } finally { + baos.close(); + } + } } diff --git a/projects/Memes/.classpath b/projects/Memes/.classpath index e91d267..0a34ec2 100644 --- a/projects/Memes/.classpath +++ b/projects/Memes/.classpath @@ -14,5 +14,10 @@ + + + + + diff --git a/projects/Memes/WebContent/WEB-INF/lib/dummy.txt b/projects/Memes/WebContent/WEB-INF/lib/dummy.txt deleted file mode 100644 index 8ac7b73..0000000 --- a/projects/Memes/WebContent/WEB-INF/lib/dummy.txt +++ /dev/null @@ -1 +0,0 @@ -Just here so that directory can exist. Fill this directory as described in the README. \ No newline at end of file diff --git a/projects/Memes/WebContent/createMeme.jsp b/projects/Memes/WebContent/createMeme.jsp index aef01d9..9e4c6d5 100644 --- a/projects/Memes/WebContent/createMeme.jsp +++ b/projects/Memes/WebContent/createMeme.jsp @@ -1,5 +1,5 @@ - -<%@page import="java.util.Date"%> -<%@page import="java.io.Console"%> -<%@page import="com.amazonaws.memes.MemeStorage"%> -<%@page import="com.amazonaws.memes.MemeCreationJob"%> -<%@page import="com.amazonaws.memes.AWSResources"%> -<%@page import="com.amazonaws.memes.S3ImageStorage"%> -<%@page import="java.util.List"%> -<%@page import="com.amazonaws.auth.BasicAWSCredentials"%> -<%@page import="com.amazonaws.auth.AWSCredentials"%> -<%@ page language="java" contentType="text/html; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> +<%@page import="com.amazonaws.memes.*"%> +<%@page import="static com.amazonaws.memes.AWSResources.*"%> +<%@page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> - -<%@ page - import="com.amazonaws.services.s3.*,com.amazonaws.services.s3.model.*"%> AWS Meme Generator - -
-
-

- See - Finished Memes - -

-

Thanks for submitting!

-

Your meme will be created shortly.

- <% - String imageKey = request.getParameter("imageKey"); - String topCaption = request.getParameter("topCaption"); - String bottomCaption = request.getParameter("bottomCaption"); - String createdBy = request.getParameter("createdBy"); - - MemeCreationJob job = new MemeCreationJob(); - job.setImageKey(imageKey); - job.setTopCaption(topCaption); - job.setBottomCaption(bottomCaption); - job.setCreationTime(new Date()); - job.setCreatedBy(createdBy); - - MemeStorage memeStorage = MemeStorage.getInstance(); - memeStorage.submitJob(job); - - out.print("

Processing..."); - out.flush(); - response.flushBuffer(); - while (!MemeCreationJob.DONE_STATUS.equals(job.getStatus()) - && !MemeCreationJob.FAILED_STATUS.equals(job.getStatus())) { - Thread.sleep(1000); - out.print("."); - out.flush(); - response.flushBuffer(); - job = memeStorage.loadJob(job.getId()); - } - if (MemeCreationJob.DONE_STATUS.equals(job.getStatus())) { - out.print(" All done!

"); - } else { - out.print(" Something went wrong :(

"); - } - %> - -

- -

- -

- Create another one! -

-
-
+
+
+

+ + See Finished Memes + +

+

Thanks for submitting!

+

Your meme will be created shortly.

+ <% + String imageKey = request.getParameter("imageKey"); + String topCaption = request.getParameter("topCaption"); + String bottomCaption = request.getParameter("bottomCaption"); + String createdBy = request.getParameter("createdBy"); + + MemeUtils memeUtils = new MemeUtils(); + ImageMacro imageMacro = memeUtils.submitJob(topCaption, bottomCaption, imageKey, createdBy); + + out.print("

Processing..."); + out.flush(); + response.flushBuffer(); + while (!ImageMacro.DONE_STATUS.equals(imageMacro.getStatus()) + && !ImageMacro.FAILED_STATUS.equals(imageMacro.getStatus())) { + Thread.sleep(1000); + out.print("."); + out.flush(); + response.flushBuffer(); + + imageMacro = DYNAMODB_MAPPER.load(ImageMacro.class, imageMacro.getId()); + } + if (ImageMacro.DONE_STATUS.equals(imageMacro.getStatus())) { + out.print(" All done!

"); + } else { + out.print(" Something went wrong :(

"); + } + %> + +

+ +

+ +

+ + Create another one! +

+
+
diff --git a/projects/Memes/WebContent/index.jsp b/projects/Memes/WebContent/index.jsp index c166bec..f8e41cd 100644 --- a/projects/Memes/WebContent/index.jsp +++ b/projects/Memes/WebContent/index.jsp @@ -1,5 +1,5 @@ - -<%@page import="com.amazonaws.memes.AWSResources"%> -<%@page import="com.amazonaws.memes.S3ImageStorage"%> -<%@page import="java.util.List"%> -<%@page import="com.amazonaws.auth.BasicAWSCredentials"%> -<%@page import="com.amazonaws.auth.AWSCredentials"%> -<%@ page language="java" contentType="text/html; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> +<%@page import="com.amazonaws.memes.*"%> +<%@page import="static com.amazonaws.memes.AWSResources.*"%> +<%@page import="com.amazonaws.services.s3.model.S3ObjectSummary"%> +<%@page import="com.amazonaws.services.s3.iterable.S3Objects"%> +<%@page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> -<%@ page - import="com.amazonaws.services.s3.*,com.amazonaws.services.s3.model.*"%> AWS Meme Generator -
-
-

- See - Finished Memes - -

-

AWS Meme Generator

-

Choose an image:

-
- <% - S3ImageStorage s3Store = new S3ImageStorage(); - boolean checked = true; - int i = 1; - for ( S3ObjectSummary summary : s3Store.getMacros() ) { - %> -
- - -
- <% - checked = false; - i++; - } - %> -
- - - - - - -

- -

-
-
-
-
+
+
+

+ + See Finished Memes + +

+

AWS Meme Generator

+

Choose an image:

+
+ <% + int i = 1; + for (S3ObjectSummary summary : S3Objects.withPrefix(S3, S3_BUCKET_NAME, MACRO_PATH)) { + %> +
+ + +
+ <% + i++; + } + %> +
+ + + + + + +

+ +

+
+
+
+
diff --git a/projects/Memes/WebContent/memes.jsp b/projects/Memes/WebContent/memes.jsp index 73af56c..b3032d3 100644 --- a/projects/Memes/WebContent/memes.jsp +++ b/projects/Memes/WebContent/memes.jsp @@ -1,5 +1,5 @@ - - -<%@page import="com.amazonaws.memes.MemeCreationJob"%> -<%@page import="com.amazonaws.memes.MemeStorage"%> -<%@page import="com.amazonaws.memes.AWSResources"%> -<%@page import="com.amazonaws.memes.S3ImageStorage"%> -<%@page import="java.util.List"%> -<%@page import="com.amazonaws.auth.BasicAWSCredentials"%> -<%@page import="com.amazonaws.auth.AWSCredentials"%> -<%@ page language="java" contentType="text/html; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> +<%@page import="com.amazonaws.memes.*"%> +<%@page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> -<%-- This is a JSP Comment before JSP Scriplet --%> -<%@ page - import="com.amazonaws.services.s3.*,com.amazonaws.services.s3.model.*"%> AWS Meme Generator -
-
-

- Create a New Meme -

-

AWS Meme Generator

-

Please enjoy these wonderful finished memes

-
-
+
+
+

+ Create a New Meme +

+

AWS Meme Generator

+

Please enjoy these wonderful finished memes

+
+
- <% - MemeStorage jobStorage = MemeStorage.getInstance(); - List jobs = jobStorage.getFinishedJobs(); - for ( MemeCreationJob job : jobs ) { - %> -
- -

- Posted by - <%=job.getCreatedBy() != null ? job.getCreatedBy() : "Anonymous" %> - on - <%=job.getCreationTime()%> -

- <% - } - %> -
-
- - + <% + MemeUtils memeUtils = new MemeUtils(); + for ( ImageMacro imageMacro : memeUtils.getFinishedJobs() ) { + %> +
+ +

+ Posted by + <%= imageMacro.getCreatedBy() != null ? imageMacro.getCreatedBy() : "Anonymous" %> + on + <%= imageMacro.getCreationTime() %> +

+ <% + } + %> +
+
+ + diff --git a/projects/Memes/src/AwsCredentials.properties b/projects/Memes/src/AwsCredentials.properties deleted file mode 100644 index be82ba6..0000000 --- a/projects/Memes/src/AwsCredentials.properties +++ /dev/null @@ -1,4 +0,0 @@ -#Insert your AWS Credentials from http://aws.amazon.com/security-credentials -#Tue Sep 25 10:22:14 PDT 2012 -secretKey=CHANGEME -accessKey=CHANGEME diff --git a/projects/Memes/src/com/amazonaws/memes/MemeUtils.java b/projects/Memes/src/com/amazonaws/memes/MemeUtils.java new file mode 100644 index 0000000..757afed --- /dev/null +++ b/projects/Memes/src/com/amazonaws/memes/MemeUtils.java @@ -0,0 +1,88 @@ +/* + * Copyright 2012-2013 Amazon Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://aws.amazon.com/apache2.0 + * + * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and + * limitations under the License. + */ +package com.amazonaws.memes; + +import static com.amazonaws.memes.AWSResources.DYNAMODB_MAPPER; +import static com.amazonaws.memes.AWSResources.FINISHED_PATH; +import static com.amazonaws.memes.AWSResources.S3_BUCKET_NAME; +import static com.amazonaws.memes.AWSResources.SQS; +import static com.amazonaws.memes.AWSResources.SQS_QUEUE_NAME; + +import java.util.Date; +import java.util.List; +import java.util.UUID; + +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression; +import com.amazonaws.services.dynamodbv2.model.AttributeValue; +import com.amazonaws.services.dynamodbv2.model.ComparisonOperator; +import com.amazonaws.services.dynamodbv2.model.Condition; +import com.amazonaws.services.sqs.model.GetQueueUrlRequest; +import com.amazonaws.services.sqs.model.SendMessageRequest; + +/** + * Helper class to manage meme creation jobs + */ +public class MemeUtils { + + public ImageMacro submitJob(String topCaption, String bottomCaption, String imageKey, String createdBy) { + ImageMacro macro = new ImageMacro(); + macro.setTopCaption(topCaption); + macro.setBottomCaption(bottomCaption); + macro.setStartingImageLink(DYNAMODB_MAPPER.createS3Link(AWSResources.S3_BUCKET_NAME, imageKey)); + macro.setFinishedImageLink(DYNAMODB_MAPPER.createS3Link(S3_BUCKET_NAME, FINISHED_PATH + UUID.randomUUID() + ".png")); + macro.setCreatedBy(createdBy); + macro.setCreationTime(new Date()); + + DYNAMODB_MAPPER.save(macro); + + /* + * Use Amazon SQS to send a message to the queue our worker processes + * are monitoring. + * + * Another option in the SDK for sending messages to a queue is the + * AmazonSQSBufferedAsyncClient. This client will buffer messages + * locally so that they are batched together in groups when they're + * sent. This means more efficient network communication with SQS + * because less individual requests with single messages are being sent. + * For high throughput applications this can not only help with + * throughput, but can also decrease your SQS costs because of the + * reduction in the amount of API calls. The tradeoff is that individual + * messages can be slightly delayed while a full batch is created on the + * client-side. + */ + String queueUrl = SQS.getQueueUrl(new GetQueueUrlRequest(SQS_QUEUE_NAME)).getQueueUrl(); + SQS.sendMessage(new SendMessageRequest(queueUrl, macro.getId())); + + return macro; + } + + /** Returns a list of the completed image macros. */ + public List getFinishedJobs() { + /* + * DynamoDBMapper allows you to run scans and queries against the + * data in your table, and interpret the results as your domain objects. + * + * In addition to unmarshalling DynamoDB results into your domain + * objects, DynamoDBMapper also takes care of result set pagination + * for you. You don't have to drop down to the low level client + * to manually manage pagination tokens. + */ + DynamoDBScanExpression scanExpression = new DynamoDBScanExpression(); + scanExpression.addFilterCondition("status", new Condition() + .withComparisonOperator(ComparisonOperator.EQ) + .withAttributeValueList(new AttributeValue(ImageMacro.DONE_STATUS))); + return DYNAMODB_MAPPER.scan(ImageMacro.class, scanExpression); + } +}