From b284307878b197c6322ea7b159c6d52d11081504 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Mon, 14 Jan 2013 12:26:05 -0500 Subject: [PATCH 1/3] Fix WebSocketEvent getter Visibility protected instead of public --- .../integration/x/ip/websocket/WebSocketEvent.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-integration-ip-extensions/src/main/java/org/springframework/integration/x/ip/websocket/WebSocketEvent.java b/spring-integration-ip-extensions/src/main/java/org/springframework/integration/x/ip/websocket/WebSocketEvent.java index 4a3a891..8130fbc 100644 --- a/spring-integration-ip-extensions/src/main/java/org/springframework/integration/x/ip/websocket/WebSocketEvent.java +++ b/spring-integration-ip-extensions/src/main/java/org/springframework/integration/x/ip/websocket/WebSocketEvent.java @@ -41,11 +41,11 @@ public class WebSocketEvent extends TcpConnectionEvent { this.queryString = queryString; } - protected String getPath() { + public String getPath() { return path; } - protected String getQueryString() { + public String getQueryString() { return queryString; } From 639d4b27e0ef1a1bb309daf1b0a84c28f29bca70 Mon Sep 17 00:00:00 2001 From: Amol Nayak Date: Sun, 19 Aug 2012 14:00:11 +0530 Subject: [PATCH 2/3] INTEXT-6: Add AWS core and SES adapter For reference see: https://jira.springsource.org/browse/INTEXT-6 --- .gitignore | 3 +- README.md | 4 +- spring-integration-aws/README.md | 141 +++++++++++ spring-integration-aws/pom.xml | 223 ++++++++++++++++++ .../aws/config/xml/AWSNamespaceHandler.java | 37 +++ ...stractAWSOutboundChannelAdapterParser.java | 68 ++++++ .../aws/config/xml/AmazonWSParserUtils.java | 98 ++++++++ .../aws/core/AWSClientFactory.java | 39 +++ .../integration/aws/core/AWSCredentials.java | 40 ++++ .../aws/core/AWSOperationException.java | 65 +++++ .../aws/core/AbstractAWSClientFactory.java | 148 ++++++++++++ .../aws/core/BasicAWSCredentials.java | 89 +++++++ .../core/InvalidAWSCredentialsException.java | 48 ++++ .../aws/core/PropertiesAWSCredentials.java | 147 ++++++++++++ .../aws/ses/AmazonSESMessageHandler.java | 51 ++++ .../xml/AmazonSESOutboundAdapterParser.java | 39 +++ .../ses/core/AmazonSESMailSendException.java | 95 ++++++++ .../ses/core/DefaultAmazonSESMailSender.java | 108 +++++++++ .../main/resources/META-INF/spring.handlers | 1 + .../main/resources/META-INF/spring.schemas | 2 + .../main/resources/META-INF/spring.tooling | 3 + .../config/xml/spring-integration-aws-1.0.xsd | 107 +++++++++ .../aws/config/xml/spring-integration-aws.gif | Bin 0 -> 539 bytes .../aws/common/AWSClientFactoryTest.java | 60 +++++ .../integration/aws/common/BaseTestCase.java | 51 ++++ ...tAWSOutboundChannelAdapterParserTests.java | 136 +++++++++++ .../core/AbstractAWSClientFactoryTests.java | 156 ++++++++++++ .../aws/ses/AmazonSESMessageHandlerTests.java | 219 +++++++++++++++++ .../AmazonSESOutboundAdapterParserTests.java | 96 ++++++++ .../DefaultAmazonSESMailSenderAWSTests.java | 132 +++++++++++ .../src/test/resources/log4j.properties | 8 + .../ses-both-awscred-props-property.xml | 18 ++ .../resources/ses-cred-ref-valid-test.xml | 21 ++ .../resources/ses-propfile-valid-test.xml | 15 ++ .../test/resources/ses-props-valid-test.xml | 17 ++ .../resources/testawscredentials.properties | 2 + 36 files changed, 2485 insertions(+), 2 deletions(-) create mode 100644 spring-integration-aws/README.md create mode 100644 spring-integration-aws/pom.xml create mode 100644 spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AWSNamespaceHandler.java create mode 100644 spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AbstractAWSOutboundChannelAdapterParser.java create mode 100644 spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AmazonWSParserUtils.java create mode 100644 spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSClientFactory.java create mode 100644 spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSCredentials.java create mode 100644 spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSOperationException.java create mode 100644 spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AbstractAWSClientFactory.java create mode 100644 spring-integration-aws/src/main/java/org/springframework/integration/aws/core/BasicAWSCredentials.java create mode 100644 spring-integration-aws/src/main/java/org/springframework/integration/aws/core/InvalidAWSCredentialsException.java create mode 100644 spring-integration-aws/src/main/java/org/springframework/integration/aws/core/PropertiesAWSCredentials.java create mode 100644 spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/AmazonSESMessageHandler.java create mode 100644 spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/config/xml/AmazonSESOutboundAdapterParser.java create mode 100644 spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/core/AmazonSESMailSendException.java create mode 100644 spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/core/DefaultAmazonSESMailSender.java create mode 100644 spring-integration-aws/src/main/resources/META-INF/spring.handlers create mode 100644 spring-integration-aws/src/main/resources/META-INF/spring.schemas create mode 100644 spring-integration-aws/src/main/resources/META-INF/spring.tooling create mode 100644 spring-integration-aws/src/main/resources/org/springframework/integration/aws/config/xml/spring-integration-aws-1.0.xsd create mode 100644 spring-integration-aws/src/main/resources/org/springframework/integration/aws/config/xml/spring-integration-aws.gif create mode 100644 spring-integration-aws/src/test/java/org/springframework/integration/aws/common/AWSClientFactoryTest.java create mode 100644 spring-integration-aws/src/test/java/org/springframework/integration/aws/common/BaseTestCase.java create mode 100644 spring-integration-aws/src/test/java/org/springframework/integration/aws/config/xml/AbstractAWSOutboundChannelAdapterParserTests.java create mode 100644 spring-integration-aws/src/test/java/org/springframework/integration/aws/core/AbstractAWSClientFactoryTests.java create mode 100644 spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/AmazonSESMessageHandlerTests.java create mode 100644 spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/config/xml/AmazonSESOutboundAdapterParserTests.java create mode 100644 spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/core/DefaultAmazonSESMailSenderAWSTests.java create mode 100644 spring-integration-aws/src/test/resources/log4j.properties create mode 100644 spring-integration-aws/src/test/resources/ses-both-awscred-props-property.xml create mode 100644 spring-integration-aws/src/test/resources/ses-cred-ref-valid-test.xml create mode 100644 spring-integration-aws/src/test/resources/ses-propfile-valid-test.xml create mode 100644 spring-integration-aws/src/test/resources/ses-props-valid-test.xml create mode 100644 spring-integration-aws/src/test/resources/testawscredentials.properties diff --git a/.gitignore b/.gitignore index 5339548..5a36a45 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,5 @@ lib logs nohup.out out -target \ No newline at end of file +target +spring-integration-aws/src/test/resources/awscredentials.properties \ No newline at end of file diff --git a/README.md b/README.md index c7dbf51..eb6b3ef 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ The Spring Integration Extensions project provides extension modules for [Spring * [Atmosphere][] Support ([Websockets][]) * [XQuery][] Support * [Splunk][] Support +* [Amazon Web Services (AWS)][] Support ## Getting support @@ -127,4 +128,5 @@ The Spring Integration Extensions Framework is released under version 2.0 of the [Atmosphere]: https://github.com/Atmosphere/atmosphere [Websockets]: http://www.html5rocks.com/en/tutorials/websockets/basics/ [XQuery]: http://en.wikipedia.org/wiki/XQuery -[Splunk]:http://www.splunk.com/ \ No newline at end of file +[Splunk]:http://www.splunk.com/ +[Amazon Web Services (AWS)]: http://aws.amazon.com/ \ No newline at end of file diff --git a/spring-integration-aws/README.md b/spring-integration-aws/README.md new file mode 100644 index 0000000..e091601 --- /dev/null +++ b/spring-integration-aws/README.md @@ -0,0 +1,141 @@ +Spring Integration Extension for Amazon Web Services (AWS) +========================================================== + + +##Small Introduction to Amazon Web Services (AWS) + + +Launched in 2006, Amazon Web Services (AWS) started providing key infrastructure for business as web services, which, also known as cloud computing. Using cloud computing businesses can adopt a new business model whereby they do not have to plan and invest in procuring their own IT infrastructure. They can use the infrastructure and services provided by the cloud service provider and pay as they use the services. +Visit [http://aws.amazon.com/products/] for more details about various products offered by Amazon as a part their cloud computing services. + +##Introduction to Spring Integration's extensions to AWS + +This guide intends to explain in brief about various adapters available for the AWS and sample xml tag definition for each of them and code snippets wherever necessary, + +The library aims to provide adapters for following AWS Services + +* **Amazon Simple Email Service (SES)** +* **Amazon Simple Storage Service (S3)** (Development complete, coming soon) +* **Amazon Simple Queue Service (SQS)** (Development complete, coming soon) +* **Amazon DynamoDB** (Analysis ongoing) +* **Amazon SimpleDB** (Not initiated) +* **Amazon SNS** (Not initiated) + +Of the above libraries, SES and SNS have outbound adapters only. All other +services have inbound and outbound adapters. The SQS inbound adapter is capable of receiving notifications sent out from SNS where the topic is an SQS Queue. +For DymamoDB and SimpleDB, apart from inbound and outbound adapters we shall provide a *MessageStore* implementation too. + + +###Executing the test cases. + +All the test cases for the adapters are present in the *src/test/java* folder. On executing the build, maven's surefire plugin will execute all +the tests. Please note that all the tests ending with **AWSTests.java* connect +to the actual webservices and are excluded by default in the maven build. +All other tests rely on mocking to test the functionality. You need to execute the **AWSTests.java* +explicitly, manually to test the conectivity to AWS using your credentials. +All these **AWSTests.java* tests look for the file *awscredentials.properties* in the classpath. +To be on the safer side create one at location *src/test/resources* as this file +*spring-integration-aws/src/test/resources/awscredentials.properties* is added to *.gitignore* file. +This will prevent this file to be checked in accidently and revealing your credentials. +This file needs to have two properties *accessKey* and *secretKey* holding the values of your access key and secret key respectively. +> **Note: AWS Services are chargeable and we recommend not to execute the **AWSTests.java* as part of your regular builds. +AWS does provide a free tier which is sufficient to perform your tests without being +charged (not true for DynamoDB though), however keep a check on your account usage regularly. +Get more information about AWS free tier at [http://aws.amazon.com/free/][]** + + + +#Adapters + +##Simple Email Service (SES) + + +###Introduction + +Amazon Simple Email Service (SES) is a web service for sending emails from the cloud. It supports two types of mails currently, the simple mail and raw email. +Use simple mail if your application just needs to send out emails with some html formatting and without embedded images or attachments. Raw emails gives more flixibility to +send complex emails with embedded images and attachments. +For more details about Amazon SES and its pricing visit [http://aws.amazon.com/ses/] +We have an outbound channel adapter for the Amazon SES Service for sending out simple and raw emails with complete namespace support for configuring the adapter. +To prevent misuse of the service, Amazon has enforced some restrictions, for sandbox you need to verify email ids which would be used in *to, cc, bcc* and *from*. For more details on how to use SES and +other details refer to the SES documentation at [http://aws.amazon.com/documentation/ses/][]. + +###Outbound Channel Adapter + +Below xml snippet is a simple definition of the outbound channel adapter + + + + + + +*propertiesFile* attribute contains the AWS access key and secret key. The +name of the properties are *accessKey* and *secretKey* respectively. +An alternative to the *propertiesFile* attribute is the *accessKey* and the +*secretKey* attributes containing the values of access key and the secret key. The definition will look as below. + + + + +Both the approaches for providing the credentials are mutually exclusive to each other. + +####Sending Mail Messages +We shall now see a java code snippet to send a mail using the SNS adapter + + +* **Simple Mail Message** + + A Simple Mail Message does not support attachments and embedded contents. It supports basic html content to be sent as the mail body. + + Map headers = new HashMap(); + headers.put("fromEmailId", "xyz@somemail.com"); + headers.put("htmlFormat", true); + headers.put("subject", "Mail Sent from AWS SES Outbound adapter"); + headers.put("toEmailId", "abc@anothermail.com"); + + Message msg = + MessageBuilder.withPayload("A Simple Mail Message sent from " + + "Amazon SES Outbound adapter from Spring integration") + .copyHeaders(headers) + .build(); + channel.send(msg); + + The above piece off code is pretty simple. + * We add four headers to the message for the *to email id*, *from email id*, *subject* and a *flag* to indicate whether the content is an html content or plain text content. + * Of these headers the *from email id* and *subject* are mandatory. We can specify one or more of *to*, *cc* or *bcc* email addresses. + * The message needs to have a payload of string which is either a plain + text or html content. The content will be rendered a html only if the + *htmlFormat *header is set appropriately. + * The value of the *htmlFormat* header can be Boolean *true*, or *y*,*yes* + or *true* as String. For String the value is case insensitive. Any other value will be considered as *false*. + * The message is sent over the channel to which is the input channel of + the outbound channel adapter. + * See *o.s.i.aws.ses.AmazonSESMailHeaders* for all the possible header values supported. + +* **Raw Mail Message** + + Use raw mail when you need more flexibility to send mails, like setting mime types and email headers. As long as the content complies with the standard email format standard you can use this means for sending the mail to your recipients. In the below sample we use the spring's *o.s.mail.javamail.MimeMessageHelper* to construct the Mime message. + + Session session = Session.getDefaultInstance(new Properties()); + MimeMessageHelper helper = new MimeMessageHelper(new MimeMessage(session),true); + helper.setTo("abc@somemail.com"); + helper.setFrom("xyz@anothermail.com"); + helper.setText("A Sample Embedded image"); + helper.addAttachment(file.getName(),new File("")); + helper.setSubject("Name Pic"); + Message message = + MessageBuilder.withPayload(helper.getMimeMessage()).build(); + channel.send(message); + + The messages over this channel are consumed by the outbound SES adapter and the mail is sent out using SES. + + +[http://aws.amazon.com/products/]: http://aws.amazon.com/products/ +[http://aws.amazon.com/ses/]: http://aws.amazon.com/ses/ +[http://aws.amazon.com/documentation/ses/]: http://aws.amazon.com/documentation/ses/ +[http://aws.amazon.com/free/]: http://aws.amazon.com/free/ \ No newline at end of file diff --git a/spring-integration-aws/pom.xml b/spring-integration-aws/pom.xml new file mode 100644 index 0000000..b868095 --- /dev/null +++ b/spring-integration-aws/pom.xml @@ -0,0 +1,223 @@ + + + 4.0.0 + org.springframework.integration + spring-integration-aws + 1.0.0.BUILD-SNAPSHOT + Spring Integration Amazon Web Services Support + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + 2.2.1 + + + + 2.2.0.RELEASE + 3.1.3.RELEASE + 1.3.12 + 1.5 + 1.1.1 + 2.0.1 + 1.4.4 + 1.9.11 + 4.1.1 + 1.1 + 4.11 + 1.9.0 + UTF8 + + + + + + src/main/java + + **/* + + + **/*.java + + + + src/main/resources + + **/* + + + + + + src/test/java + + **/* + + + **/*.java + + + + src/test/resources + + **/* + + + + + + maven-compiler-plugin + + 1.6 + 1.6 + + + + maven-surefire-plugin + + + **/*Tests.java + + + **/*Abstract*.java + **/*AWSTests.java + + + + + + + + springsource-libs-milestone + Spring Framework Maven Milestone Repository + https://repo.springsource.org/libs-milestone + + + + + com.amazonaws + aws-java-sdk + ${aws.sdk.version} + compile + + + + commons-codec + commons-codec + ${commons.codec.version} + compile + + + + commons-logging + commons-logging + ${commons.logging.version} + compile + + + + commons-io + commons-io + ${commons.io.version} + compile + + + + javax.mail + mail + ${java.mail.version} + compile + + + + org.springframework.integration + spring-integration-core + ${spring.integration.version} + compile + + + + org.springframework.integration + spring-integration-mail + ${spring.integration.version} + compile + + + + + org.codehaus.jackson + jackson-core-asl + ${jackson.version} + compile + + + + org.codehaus.jackson + jackson-mapper-asl + ${jackson.version} + compile + + + + org.apache.httpcomponents + httpclient + ${apache.httpclient.version} + compile + + + + org.apache.httpcomponents + httpcore + ${apache.httpclient.version} + compile + + + + org.springframework.integration + spring-integration-test + ${spring.integration.version} + test + + + + org.hamcrest + hamcrest-core + ${hamcrest.version} + test + + + + junit + junit + ${junit.version} + test + + + + org.mockito + mockito-all + ${mockito.version} + test + + + + org.mockito + mockito-core + ${mockito.version} + test + + + + org.springframework + spring-test + ${spring.test.version} + test + + + + diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AWSNamespaceHandler.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AWSNamespaceHandler.java new file mode 100644 index 0000000..044e702 --- /dev/null +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AWSNamespaceHandler.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.config.xml; + +import org.springframework.integration.aws.ses.config.xml.AmazonSESOutboundAdapterParser; +import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler; + +/** + * The namepsace handler for "int-aws" namespace + * + * @author Amol Nayak + * + * @since 1.0 + * + */ +public class AWSNamespaceHandler extends + AbstractIntegrationNamespaceHandler { + + + public void init() { + this.registerBeanDefinitionParser("ses-outbound-channel-adapter", new AmazonSESOutboundAdapterParser()); + } + +} diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AbstractAWSOutboundChannelAdapterParser.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AbstractAWSOutboundChannelAdapterParser.java new file mode 100644 index 0000000..3ea2a57 --- /dev/null +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AbstractAWSOutboundChannelAdapterParser.java @@ -0,0 +1,68 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.config.xml; + +import static org.springframework.integration.aws.config.xml.AmazonWSParserUtils.getAmazonWSCredentials; + +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; +import org.springframework.integration.core.MessageHandler; +import org.w3c.dom.Element; + +/** + * The common adapter parser for all AWS Outbound channel adapters + * + * @author Amol Nayak + * + * @since 1.0 + * + */ +public abstract class AbstractAWSOutboundChannelAdapterParser extends + AbstractOutboundChannelAdapterParser { + + /* (non-Javadoc) + * @see org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser#parseConsumer(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext) + */ + @Override + protected final AbstractBeanDefinition parseConsumer(Element element, + ParserContext parserContext) { + String awsCredentialsGeneratedName = getAmazonWSCredentials(element,parserContext); + + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(getMessageHandlerImplementation()); + builder.addConstructorArgReference(awsCredentialsGeneratedName); + processBeanDefinition(builder,awsCredentialsGeneratedName,element,parserContext); + return builder.getBeanDefinition(); + } + + protected abstract Class getMessageHandlerImplementation(); + + /** + * The subclasses can override this method to set additional attributes and perform some + * additional operations on the {@link BeanDefinitionBuilder} + * + * @param builder + * @param awsCredentialsGeneratedName + * @param element + * @param context + */ + protected void processBeanDefinition(BeanDefinitionBuilder builder,String awsCredentialsGeneratedName, + Element element,ParserContext context) { + //Default implementation does nothing + } + +} diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AmazonWSParserUtils.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AmazonWSParserUtils.java new file mode 100644 index 0000000..974550e --- /dev/null +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AmazonWSParserUtils.java @@ -0,0 +1,98 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.config.xml; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.aws.core.BasicAWSCredentials; +import org.springframework.integration.aws.core.PropertiesAWSCredentials; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * The utility class for the namespace parsers + * + * @author Amol Nayak + * + * @since 1.0 + * + */ +public final class AmazonWSParserUtils { + + public static final String ACCESS_KEY = "accessKey"; + public static final String SECRET_KEY = "secretKey"; + public static final String PROPERTIES_FILE = "propertiesFile"; + public static final String CREDENTIALS_REF = "credentials-ref"; + + private AmazonWSParserUtils() { + throw new AssertionError("Cannot instantiate the utility class"); + } + + + /** + * Registers the {@link AmazonWSCredentials} bean with the current ApplicationContext if + * accessKey and secretKey is given, if the credentials-ref is given, the given value + * is returned. + * + * @param element + * @param parserContext + * @return + */ + public static String getAmazonWSCredentials(Element element,ParserContext parserContext) { + //TODO: Some mechanism to use the same instance with same ACCESS_KEY to be implemented + String accessKey = element.getAttribute(ACCESS_KEY); + String secretKey = element.getAttribute(SECRET_KEY); + String propertiesFile = element.getAttribute(PROPERTIES_FILE); + String credentialsRef = element.getAttribute(CREDENTIALS_REF); + String awsCredentialsGeneratedName; + + if(StringUtils.hasText(credentialsRef)) { + if(StringUtils.hasText(propertiesFile) + || StringUtils.hasText(accessKey) + || StringUtils.hasText(secretKey)) { + parserContext.getReaderContext().error("When " + CREDENTIALS_REF + " is specified, " + + "do not specify the " + PROPERTIES_FILE + " attribute or the " + + SECRET_KEY + " and " + ACCESS_KEY + " attributes", element); + } + awsCredentialsGeneratedName = credentialsRef; + } + else { + if(StringUtils.hasText(propertiesFile)) { + if(StringUtils.hasText(accessKey) && StringUtils.hasText(secretKey)) { + parserContext.getReaderContext().error("When " + ACCESS_KEY + " and " + SECRET_KEY + + " are specified, do not specify the " + PROPERTIES_FILE + " attribute", element); + } + + BeanDefinitionBuilder builder = + BeanDefinitionBuilder.genericBeanDefinition(PropertiesAWSCredentials.class); + builder.addConstructorArgValue(propertiesFile); + awsCredentialsGeneratedName = BeanDefinitionReaderUtils.registerWithGeneratedName( + builder.getBeanDefinition(), parserContext.getRegistry()); + } else { + BeanDefinitionBuilder builder + = BeanDefinitionBuilder.genericBeanDefinition(BasicAWSCredentials.class); + builder.addConstructorArgValue(accessKey); + builder.addConstructorArgValue(secretKey); + awsCredentialsGeneratedName = BeanDefinitionReaderUtils.registerWithGeneratedName( + builder.getBeanDefinition(), parserContext.getRegistry()); + + } + } + + return awsCredentialsGeneratedName; + } +} diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSClientFactory.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSClientFactory.java new file mode 100644 index 0000000..1c74cbe --- /dev/null +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSClientFactory.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.core; + +import com.amazonaws.AmazonWebServiceClient; + +/** + * The factory interface that would be used to get the implementation of the appropriate + * instance of {@link AmazonWebServiceClient} + * + * @author Amol Nayak + * + * @since 1.0 + * + */ +public interface AWSClientFactory { + + /** + * Returns the instance of the {@link AmazonWebServiceClient} with the apropriate endpoint value + * set based on the provided url value + * + * @param url The url of the service + * @return The appropriate {@link AmazonWebServiceClient} for the provided endpoint URL + */ + T getClient(String url); +} diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSCredentials.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSCredentials.java new file mode 100644 index 0000000..664f231 --- /dev/null +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSCredentials.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.core; + +/** + * The common interfaces for all implementations of Amazon WS Credentials + * + * @author Amol Nayak + * + * @since 1.0 + * + */ +public interface AWSCredentials { + + /** + * Get the Access key to the Amazon WS account + * @return + */ + public String getAccessKey(); + + /** + * Get the Secret key to the Amazon WS account + * @return + */ + public String getSecretKey(); + +} diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSOperationException.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSOperationException.java new file mode 100644 index 0000000..1b9035d --- /dev/null +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSOperationException.java @@ -0,0 +1,65 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.core; + +/** + * The Base class for all other AWS operation exceptions + * + * @author Amol Nayak + * + * @since 1.0 + * + */ +public class AWSOperationException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = 3391888045993691634L; + + private final String accessKey; + + public AWSOperationException(String accessKey) { + super(); + this.accessKey = accessKey; + } + + public AWSOperationException(String accessKey,String message) { + super(message); + this.accessKey = accessKey; + } + + public AWSOperationException(String accessKey,String message, Throwable cause) { + super(message, cause); + this.accessKey = accessKey; + } + + public AWSOperationException(String accessKey,Throwable cause) { + super(cause); + this.accessKey = accessKey; + } + + /** + * Get the access key for the user who encountered the exception + * @return + */ + public String getAccessKey() { + return accessKey; + } + + + +} diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AbstractAWSClientFactory.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AbstractAWSClientFactory.java new file mode 100644 index 0000000..e630607 --- /dev/null +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AbstractAWSClientFactory.java @@ -0,0 +1,148 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.core; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +import com.amazonaws.AmazonWebServiceClient; + + +/** + * The abstract factory class that will be used by all the client operations to acquire + * the appropriate implementation of the {@link AmazonWebServiceClient} based on the URL + * passed to the getClient method + * + * @author Amol Nayak + * + * @since 1.0 + * + */ +public abstract class AbstractAWSClientFactory implements AWSClientFactory { + + /** + * A map storing the {@link AmazonWebServiceClient} endpoint as the key and the SQS Client as the + * value. Since setting the endpoint is not a thread safe operation once the client is instantiated, + * we maintain a map of endpoint and the {@link AmazonWebServiceClient} instantiated the first time a request + * for the designated endpoint is received + */ + private final ConcurrentHashMap clientMap = new ConcurrentHashMap(); + + /** + * The String constant for HTTP + */ + protected final static String HTTP = "http://"; + + /** + * The String constant for HTTPS + */ + protected final static String HTTPS = "https://"; + + /** + * The String constant for SMTP + */ + protected final String SMTP = "smtp://"; + + /** + * The default protocol to be used in case none is provided + */ + protected final static String DEFAULT_PROTOCOL = HTTPS; + + + /** + * Returns the cached implementation of the {@link AmazonWebServiceClient} based on the URL provided. + * the client instance is acquired using the abstract getClientImplementation method. + * The instance is added to the client map with the endpoint string as the key and the + * {@link AmazonWebServiceClient} as the value. + * + * @param url the URL for which the client is requested. + * @return the implementation of the {@link AmazonWebServiceClient} to be used for the provided url + */ + public final T getClient(String url) { + String endpoint = getEndpointFromURL(url); + if(!clientMap.containsKey(endpoint)) { + T client = getClientImplementation(); + client.setEndpoint(endpoint); + T existingClient = clientMap.putIfAbsent(endpoint, client); + if(existingClient != null) { + //in rare scenarios where a new implementation was created after + //checking for the existence of the endpoint in the client map + client = existingClient; + } + return client; + } else + return clientMap.get(endpoint); + } + + /** + * Return a copy of the client map + * @return the copy of the clientMap + */ + public final Map getClientMap() { + return new HashMap(clientMap); + } + + /** + * Clears the complete cache + */ + public final void clear() { + clientMap.clear(); + } + + /** + * Extracts the endpoint from the URL provided + * + * @param url + * @return + */ + private String getEndpointFromURL(String stringUrl) { + Assert.notNull(stringUrl,"Provided String URL is null"); + String endpoint; + try { + if(!(stringUrl.startsWith(HTTP) + || stringUrl.startsWith(HTTPS) + || stringUrl.startsWith(SMTP))) { + stringUrl = DEFAULT_PROTOCOL + stringUrl; + } + URL url = new URL(stringUrl); + String host = url.getHost(); + String protocol = url.getProtocol(); + if(StringUtils.hasText(protocol)) { + endpoint = protocol + "://" + host; + } + else { + endpoint = host; + } + } catch (MalformedURLException e) { + throw new AWSOperationException(null, "The URL \"" + stringUrl + "\" is malformed",e); + } + return endpoint; + } + + /** + * The subclass needs to implement this method and return an appropriate implementation + * @return + */ + protected abstract T getClientImplementation(); + + +} diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/BasicAWSCredentials.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/BasicAWSCredentials.java new file mode 100644 index 0000000..69cebea --- /dev/null +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/BasicAWSCredentials.java @@ -0,0 +1,89 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.core; + +/** + * The basic implementation class holding the Access key and the secret + * key for the AWS account . + * + * @author Amol Nayak + * + * @since 1.0 + * + */ +public class BasicAWSCredentials implements AWSCredentials { + + /**. + * Hold the Access key for the AWS account + */ + private String accessKey; + + /**. + * Hold the Secret key for the + */ + private String secretKey; + + + /**. + * Default constructor + */ + public BasicAWSCredentials() { + + } + + /**. + * The constructor accepting the access and secret key + * @param accessKey + * @param secretKey + */ + public BasicAWSCredentials(String accessKey, String secretKey) { + super(); + this.accessKey = accessKey; + this.secretKey = secretKey; + } + + /** + * Get the Access key to the Amazon WS account + * @return + */ + public String getAccessKey() { + return accessKey; + } + + /** + * Set the Access key to the Amazon WS account + * @param accessKey + */ + public void setAccessKey(String accessKey) { + this.accessKey = accessKey; + } + + /** + * Get the Secret key to the Amazon WS account + * @return + */ + public String getSecretKey() { + return secretKey; + } + + /**. + * Set the Secret key to the Amazon WS account + * @param secretKey + */ + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } +} diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/InvalidAWSCredentialsException.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/InvalidAWSCredentialsException.java new file mode 100644 index 0000000..408c916 --- /dev/null +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/InvalidAWSCredentialsException.java @@ -0,0 +1,48 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.core; + +/** + * Thrown when AWS Credentials provided by the user are incomplete or invalid + * + * @author Amol Nayak + * + * @since 1.0 + * + */ +public class InvalidAWSCredentialsException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = 1L; + + public InvalidAWSCredentialsException() { + super(); + } + + public InvalidAWSCredentialsException(String message, Throwable cause) { + super(message, cause); + } + + public InvalidAWSCredentialsException(String message) { + super(message); + } + + public InvalidAWSCredentialsException(Throwable cause) { + super(cause); + } +} diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/PropertiesAWSCredentials.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/PropertiesAWSCredentials.java new file mode 100644 index 0000000..2f2ef39 --- /dev/null +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/PropertiesAWSCredentials.java @@ -0,0 +1,147 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.core; + +import java.io.IOException; +import java.util.Properties; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.util.ClassUtils; +import org.springframework.util.ResourceUtils; +import org.springframework.util.StringUtils; + +/** + * Load the AWS credentials from the .properties file + * + * @author Amol Nayak + * + * @since 1.0 + * + */ +public class PropertiesAWSCredentials extends BasicAWSCredentials implements InitializingBean { + + + public static final String DEFAULT_AWS_ACCESS_KEY_PROPERTY = "accessKey"; + public static final String DEFAULT_AWS_SECRET_KEY_PROPERTY = "secretKey"; + + private String accessKeyProperty = DEFAULT_AWS_ACCESS_KEY_PROPERTY; + private String secretKeyProperty = DEFAULT_AWS_SECRET_KEY_PROPERTY; + private String propertyFileName; + + /**. + * Constructor accepting the properties file + * @param propertyFileName + */ + public PropertiesAWSCredentials(String propertyFileName) { + super(); + this.propertyFileName = propertyFileName; + } + + /**. + * Gets the property name which holds the + * @return + */ + public String getAccessKeyProperty() { + return accessKeyProperty; + } + + /**. + * Sets the name of the property that will be used as the key in the properties + * file to hold the AWS access key + * @param accessKeyProperty + */ + public void setAccessKeyProperty(String accessKeyProperty) { + this.accessKeyProperty = accessKeyProperty; + } + + /**. + * Gets the property name which holds the + * @return + */ + public String getSecretKeyProperty() { + return secretKeyProperty; + } + + /**. + * Sets the name of the property that will be used as the key in the properties + * file to hold the AWS secret key + * @param accessKeyProperty + */ + public void setSecretKeyProperty(String secretKeyProperty) { + this.secretKeyProperty = secretKeyProperty; + } + + /**. + * Get the name of the property file that will hold the AWS credentials + * @return + */ + public String getPropertyFileName() { + return propertyFileName; + } + + /**. + * Sets the name of the file that will hold the AW credentials + * @param propertyFileName + */ + public void setPropertyFileName(String propertyFileName) { + this.propertyFileName = propertyFileName; + } + + /**. + * Load the properties file and the keys + */ + public void afterPropertiesSet() throws Exception { + if (!StringUtils.hasText(propertyFileName)) + throw new InvalidAWSCredentialsException("Mandatory property propertyFileName expected"); + + if(!StringUtils.hasText(accessKeyProperty)) + throw new InvalidAWSCredentialsException("accessKeyValue has to be non empty and non null"); + + if(!StringUtils.hasText(secretKeyProperty)) + throw new InvalidAWSCredentialsException("secretKeyValue has to be non empty and non null"); + + loadProperties(); + + } + + /**. + * The private method that loads the properties from the .properties file and sets the access keys + */ + private void loadProperties() { + Resource resource; + if(propertyFileName.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)) { + resource = new ClassPathResource(propertyFileName.substring(ResourceUtils.CLASSPATH_URL_PREFIX.length()), ClassUtils.getDefaultClassLoader()); + } else { + resource = new ClassPathResource(propertyFileName, ClassUtils.getDefaultClassLoader()); + } + if(!resource.exists()) + throw new InvalidAWSCredentialsException("Unable to find resource \"" + propertyFileName + "\" in classpath"); + + Properties props = new Properties(); + try { + props.load(resource.getInputStream()); + } catch (IOException e) { + throw new InvalidAWSCredentialsException("Unable to load properties from \"" + propertyFileName + "\" in classpath"); + } + + setAccessKey((String)props.get(accessKeyProperty)); + setSecretKey((String)props.get(secretKeyProperty)); + + + } +} diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/AmazonSESMessageHandler.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/AmazonSESMessageHandler.java new file mode 100644 index 0000000..0403c7d --- /dev/null +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/AmazonSESMessageHandler.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.ses; + +import org.springframework.integration.aws.core.AWSCredentials; +import org.springframework.integration.aws.ses.core.DefaultAmazonSESMailSender; +import org.springframework.integration.mail.MailSendingMessageHandler; +import org.springframework.mail.javamail.JavaMailSender; + +/** + * The Message handler for the SES Mail. This will be used to send email + * using Amazon SES + * + * @author Amol Nayak + * + * @since 1.0 + * + */ +public class AmazonSESMessageHandler extends MailSendingMessageHandler { + + /** + * The Default constructor that extends from the {@link MailSendingMessageHandler} and passes + * it an instance of {@link DefaultAmazonSESMailSender} + * @param credentials + */ + public AmazonSESMessageHandler(AWSCredentials credentials) { + super(new DefaultAmazonSESMailSender(credentials)); + } + + /** + * The constructor that accepts the {@link JavaMailSender} instance, used for + * unit tests only + * @param mailSender + */ + AmazonSESMessageHandler(JavaMailSender mailSender) { + super(mailSender); + } +} diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/config/xml/AmazonSESOutboundAdapterParser.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/config/xml/AmazonSESOutboundAdapterParser.java new file mode 100644 index 0000000..8ea1cae --- /dev/null +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/config/xml/AmazonSESOutboundAdapterParser.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.ses.config.xml; + + +import org.springframework.integration.aws.config.xml.AbstractAWSOutboundChannelAdapterParser; +import org.springframework.integration.aws.ses.AmazonSESMessageHandler; +import org.springframework.integration.core.MessageHandler; + +/** + * parse the <ses-outbound-channel-adapter/> of the "int-aws" namespace + * + * @author Amol Nayak + * + * @since 1.0 + * + */ +public class AmazonSESOutboundAdapterParser extends + AbstractAWSOutboundChannelAdapterParser { + + + @Override + public Class getMessageHandlerImplementation() { + return AmazonSESMessageHandler.class; + } +} diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/core/AmazonSESMailSendException.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/core/AmazonSESMailSendException.java new file mode 100644 index 0000000..a28018a --- /dev/null +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/core/AmazonSESMailSendException.java @@ -0,0 +1,95 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.ses.core; + +import java.util.Map; + +import org.springframework.integration.aws.core.AWSOperationException; + +/** + * This exception will be thrown upon failure in sending a mail from Amazon SES + * + * @author Amol Nayak + * + * @since 1.0 + * + */ +public class AmazonSESMailSendException extends AWSOperationException { + + + /** + * + */ + private static final long serialVersionUID = -3035267174544370619L; + + private final Map failedMessages; + + /** + * + * @param accessKey + * @param message + * @param cause + * @param failedMessages + */ + public AmazonSESMailSendException(String accessKey, String message, + Throwable cause,Map failedMessages) { + super(accessKey, message, cause); + this.failedMessages = failedMessages; + } + + /** + * + * @param accessKey + * @param message + * @param failedMessages + */ + public AmazonSESMailSendException(String accessKey, + String message,Map failedMessages) { + super(accessKey, message); + this.failedMessages = failedMessages; + } + + /** + * + * @param accessKey + * @param cause + * @param failedMessages + */ + public AmazonSESMailSendException(String accessKey, + Throwable cause,Map failedMessages) { + super(accessKey, cause); + this.failedMessages = failedMessages; + } + + /** + * + * @param accessKey + * @param failedMessages + */ + public AmazonSESMailSendException(String accessKey,Map failedMessages) { + super(accessKey); + this.failedMessages = failedMessages; + } + + /** + * Gets the map of failed messages where the failed message is the key and the exception + * while sending it is the value + * @return + */ + public Map getFailedMessages() { + return failedMessages; + } +} diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/core/DefaultAmazonSESMailSender.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/core/DefaultAmazonSESMailSender.java new file mode 100644 index 0000000..9fdc60e --- /dev/null +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/core/DefaultAmazonSESMailSender.java @@ -0,0 +1,108 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.ses.core; + +import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +import javax.mail.internet.MimeMessage; + +import org.springframework.integration.aws.core.AWSCredentials; +import org.springframework.integration.aws.core.AWSOperationException; +import org.springframework.mail.MailException; +import org.springframework.mail.javamail.JavaMailSenderImpl; + +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClient; +import com.amazonaws.services.simpleemail.model.RawMessage; +import com.amazonaws.services.simpleemail.model.SendRawEmailRequest; + +/** + * The implementation class for sending mail using the Amazon SES service + * + * @author Amol Nayak + * + * @since 1.0 + * + */ +public class DefaultAmazonSESMailSender extends JavaMailSenderImpl { + + /**. + * The API class used for sending email using Amazon SES + */ + private final AmazonSimpleEmailServiceClient emailService; + + private final AWSCredentials credentials; + + public DefaultAmazonSESMailSender(AWSCredentials credentials) { + if(credentials == null) + throw new AWSOperationException(null, "Credentials cannot be null, provide a non null valid set of credentials"); + this.credentials = credentials; + + emailService = new AmazonSimpleEmailServiceClient( + new BasicAWSCredentials(credentials.getAccessKey(), + credentials.getSecretKey())); + } + + + /** + * The Actual implementation of the sending of the mail message using the AWS SES + * SDK + */ + @Override + protected void doSend(MimeMessage[] mimeMessages, Object[] originalMessages) + throws MailException { + Map failedMessageMap = new HashMap(); + if(mimeMessages != null && mimeMessages.length > 0) { + int index = 0; + for(MimeMessage mailMessage:mimeMessages) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + mailMessage.writeTo(baos); + RawMessage rawMessage = new RawMessage(ByteBuffer.wrap(baos.toByteArray())); + SendRawEmailRequest rawMail = new SendRawEmailRequest(rawMessage); + emailService.sendRawEmail(rawMail); + index++; + } catch (Exception e) { + failedMessageMap.put(originalMessages != null? originalMessages[index]:mailMessage, e); + } + } + } + + if(!failedMessageMap.isEmpty()) { + throw new AmazonSESMailSendException(credentials.getAccessKey(), + "Exception while sending one or more messages, see failedMessages for more details", + failedMessageMap); + } + } + + @Override + public void setPort(int port) { + throw new UnsupportedOperationException("AWS SES Implementation of mail " + + "sender does not allow setting the port number"); + } + + + @Override + public void setHost(String host) { + throw new UnsupportedOperationException("AWS SES Implementation of mail " + + "sender does not allow setting the host name. It is already configured with an endpoint"); + } + + //Should we disallow setSession? +} \ No newline at end of file diff --git a/spring-integration-aws/src/main/resources/META-INF/spring.handlers b/spring-integration-aws/src/main/resources/META-INF/spring.handlers new file mode 100644 index 0000000..83be3bf --- /dev/null +++ b/spring-integration-aws/src/main/resources/META-INF/spring.handlers @@ -0,0 +1 @@ +http\://www.springframework.org/schema/integration/aws=org.springframework.integration.aws.config.xml.AWSNamespaceHandler diff --git a/spring-integration-aws/src/main/resources/META-INF/spring.schemas b/spring-integration-aws/src/main/resources/META-INF/spring.schemas new file mode 100644 index 0000000..4e9c49d --- /dev/null +++ b/spring-integration-aws/src/main/resources/META-INF/spring.schemas @@ -0,0 +1,2 @@ +http\://www.springframework.org/schema/integration/aws/spring-integration-aws-1.0.xsd=org/springframework/integration/aws/config/xml/spring-integration-aws-1.0.xsd +http\://www.springframework.org/schema/integration/aws/spring-integration-aws.xsd=org/springframework/integration/aws/config/xml/spring-integration-aws-1.0.xsd diff --git a/spring-integration-aws/src/main/resources/META-INF/spring.tooling b/spring-integration-aws/src/main/resources/META-INF/spring.tooling new file mode 100644 index 0000000..878f9cd --- /dev/null +++ b/spring-integration-aws/src/main/resources/META-INF/spring.tooling @@ -0,0 +1,3 @@ +http\://www.springframework.org/schema/integration/aws@name=Integration AWS Namespace +http\://www.springframework.org/schema/integration/aws@prefix=int-aws +http\://www.springframework.org/schema/integration/aws@icon=org/springframework/integration/aws/config/xml/spring-integration-aws.gif \ No newline at end of file diff --git a/spring-integration-aws/src/main/resources/org/springframework/integration/aws/config/xml/spring-integration-aws-1.0.xsd b/spring-integration-aws/src/main/resources/org/springframework/integration/aws/config/xml/spring-integration-aws-1.0.xsd new file mode 100644 index 0000000..9dc4dd7 --- /dev/null +++ b/spring-integration-aws/src/main/resources/org/springframework/integration/aws/config/xml/spring-integration-aws-1.0.xsd @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + Specify the access key to be used to connect to the AWS service. + + + + + + + Specify the secret key corresponding to the access key to be used to + authenticate the user and connect to the AWS service. + + + + + + + Specifies the properties file used that will be used to hold the access key + and the secret key of the AWS. This file expects two properties accessKey and + secretKey for Access key and Secret Key respectively. This attribute is mutually + exclusive to the accessKey, secretKey and credentials-ref attribute. + + + + + + + + + + + + If none of the default mechanisms work, you may provide a custom implementation + of org.springframework.integration.aws.core.AWSCredentials. Various use cases are + when you might want to read the credentials from a database or any other secure store + + + + + + + + + + Defines an outbound mail-sending Channel Adapter for sending mails using Amazon SES. + + + + + + + + + + + + Identifies channel attached to this adapter. This is the channel over which + the SES outbound adapter will receive messages from. The received message + will then be converted to the email to be sent. + + + + + + + + Lifecycle attribute signaling if this component should be started during + Application Context startup. Default is 'true' + + + + + + + + Specifies the order for invocation when this endpoint is connected as a + subscriber to a SubscribableChannel. + + + + + + \ No newline at end of file diff --git a/spring-integration-aws/src/main/resources/org/springframework/integration/aws/config/xml/spring-integration-aws.gif b/spring-integration-aws/src/main/resources/org/springframework/integration/aws/config/xml/spring-integration-aws.gif new file mode 100644 index 0000000000000000000000000000000000000000..210e0764fa4c1e5baebcdea156bc3f3e1f97a2c9 GIT binary patch literal 539 zcmZ?wbhEHb6krfwc*Xz%|NsB*n7VH6iaomzUp%y<^XRV5le@dm?CrjBr0?SXo|o4a z>{}Ri^GM%=bBkWz+y8vM?x%h3?+zusxj5zR@#^Jm-pe}!R`mow*jzjz+p;&)qBX&& zG1jmr&Ac*H`_zJhTu;?PU-d#?%_oZ!?=4DAaa1VqRO?H!xIQD{=A4w7g+5uX8WR(2 zrX<_tc8Bb5QRHvpiB8JU`8KTWP?thKQZD zFcaE=iuVx<>PO@dH?gn{*FclYXGfDn1BEP literal 0 HcmV?d00001 diff --git a/spring-integration-aws/src/test/java/org/springframework/integration/aws/common/AWSClientFactoryTest.java b/spring-integration-aws/src/test/java/org/springframework/integration/aws/common/AWSClientFactoryTest.java new file mode 100644 index 0000000..a3a44d5 --- /dev/null +++ b/spring-integration-aws/src/test/java/org/springframework/integration/aws/common/AWSClientFactoryTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.common; + +import junit.framework.Assert; + +import org.junit.Test; +import org.springframework.integration.aws.core.AbstractAWSClientFactory; + +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.services.sqs.AmazonSQSClient; + +/** + * The test class for {@link AbstractAmazonWSClientFactory} + * + * @author Amol Nayak + * + * @since 1.0 + * + */ +public class AWSClientFactoryTest { + + AbstractAWSClientFactory factory = new AbstractAWSClientFactory() { + + @Override + protected AmazonSQSClient getClientImplementation() { + return new AmazonSQSClient((AWSCredentials)null); + } + + }; + + @Test + public void getRegionSpecificEndpoints() { + //TODO: Use Spring integration test to inspect and assert the variables within + AmazonSQSClient usEastClient = factory.getClient("https://queue.amazonaws.com/123456789012/MyTestQueue"); + Assert.assertNotNull(usEastClient); + Assert.assertEquals(factory.getClientMap().size(),1); + AmazonSQSClient usEastClient1 = factory.getClient("https://queue.amazonaws.com/123456789012/MyTestQueue1"); + Assert.assertNotNull(usEastClient1); + Assert.assertEquals(usEastClient,usEastClient1); + Assert.assertEquals(factory.getClientMap().size(),1); + AmazonSQSClient apacClient1 = factory.getClient("https://ap-southeast-1.queue.amazonaws.com/123456789012/MyApacTestQueue"); + Assert.assertNotNull(apacClient1); + Assert.assertEquals(factory.getClientMap().size(),2); + + } +} diff --git a/spring-integration-aws/src/test/java/org/springframework/integration/aws/common/BaseTestCase.java b/spring-integration-aws/src/test/java/org/springframework/integration/aws/common/BaseTestCase.java new file mode 100644 index 0000000..91e501b --- /dev/null +++ b/spring-integration-aws/src/test/java/org/springframework/integration/aws/common/BaseTestCase.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.common; + +import java.io.IOException; +import java.util.Properties; + +/** + * Class extended by some of the test cases which are not spring based to get access to the properties + * and other common functionality + * + * @author Amol Nayak + * + * @since 1.0 + * + */ +public abstract class BaseTestCase { + + protected static String propsLocation = "testprops.properties"; + protected static Properties props; + static { + props = new Properties(); + try { + props.load(ClassLoader.getSystemClassLoader().getResourceAsStream(propsLocation)); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + protected static String getProperty(String key) { + if(props != null) + return props.getProperty(key); + else + return null; + } + +} diff --git a/spring-integration-aws/src/test/java/org/springframework/integration/aws/config/xml/AbstractAWSOutboundChannelAdapterParserTests.java b/spring-integration-aws/src/test/java/org/springframework/integration/aws/config/xml/AbstractAWSOutboundChannelAdapterParserTests.java new file mode 100644 index 0000000..62f2501 --- /dev/null +++ b/spring-integration-aws/src/test/java/org/springframework/integration/aws/config/xml/AbstractAWSOutboundChannelAdapterParserTests.java @@ -0,0 +1,136 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.config.xml; + +import junit.framework.Assert; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.aws.core.AWSCredentials; +import org.springframework.integration.core.MessageHandler; +import org.springframework.integration.endpoint.AbstractEndpoint; +import org.springframework.integration.test.util.TestUtils; + +/** + * The Abstract test class for the AWS outbound adapter parsers + * + * @author Amol Nayak + * + * @since 1.0 + * + */ +public abstract class AbstractAWSOutboundChannelAdapterParserTests { + + protected static ClassPathXmlApplicationContext ctx; + private static boolean isInitialized; + + /** + * The bean name expected to be present in the context which would be used when the + * element is defined with the propertiesFile attribute + */ + private final String CREDENTIALS_TEST_ONE = "credentialsTestOne"; + + /** + * The bean name expected to be present in the context which would be used when the + * element is defined with the accessKey and the secretKey definition + */ + private final String CREDENTIALS_TEST_TWO = "credentialsTestTwo"; + + + @Before + public void setup() { + if(!isInitialized) { + String contextLocation = getConfigFilePath(); + Assert.assertNotNull("Non null path value expected", contextLocation); + ctx = new ClassPathXmlApplicationContext(contextLocation); + Assert.assertTrue("Bean with id " + CREDENTIALS_TEST_ONE + + " expected to be present in the context for credentials test", + ctx.containsBean(CREDENTIALS_TEST_ONE)); + Assert.assertTrue("Bean with id " + CREDENTIALS_TEST_TWO + + " expected to be present in the context for credentials test", + ctx.containsBean(CREDENTIALS_TEST_TWO)); + isInitialized = true; + + } + } + + @AfterClass + public static void destroy() { + ctx.close(); + } + + /** + * Gets the Message handler implementation for the given bean id + * @param beanId + * @return + */ + @SuppressWarnings("unchecked") + protected T getMessageHandlerForBeanDefinition(String beanId) { + AbstractEndpoint endpoint = (AbstractEndpoint)ctx.getBean(beanId); + return (T)TestUtils.getPropertyValue(endpoint, "handler"); + } + + + /** + * Gets the config file path that would be used to create the {@link ApplicationContext} instance + * sub class should implement this method to return a value of the config to be used to create the + * context + * + * @return + */ + protected abstract String getConfigFilePath(); + + /** + * The subclass should return the {@link AmazonWSCredentials} instance that is being used + * to configure the adapters + * + * @return + */ + protected abstract AWSCredentials getCredentials(); + + /** + * The test class for the AWS Credentials test that used property files + */ + @Test + public final void awsCredentialsTestWithPropFiles() { + MessageHandler handler = getMessageHandlerForBeanDefinition(CREDENTIALS_TEST_ONE); + AWSCredentials credentials = getCredentials(); + String accessKey = credentials.getAccessKey(); + String secretKey = credentials.getSecretKey(); + AWSCredentials configuredCredentials = + TestUtils.getPropertyValue(handler, "credentials",AWSCredentials.class); + Assert.assertEquals(accessKey, configuredCredentials.getAccessKey()); + Assert.assertEquals(secretKey, configuredCredentials.getSecretKey()); + } + + /** + * The test class for the AWS Credentials test that used accessKey and secretKey elements + */ + @Test + public final void awsCredentialsTestWithoutPropFiles() { + MessageHandler handler = getMessageHandlerForBeanDefinition(CREDENTIALS_TEST_TWO); + AWSCredentials credentials = getCredentials(); + String accessKey = credentials.getAccessKey(); + String secretKey = credentials.getSecretKey(); + AWSCredentials configuredCredentials = + TestUtils.getPropertyValue(handler, "credentials",AWSCredentials.class); + Assert.assertEquals(accessKey, configuredCredentials.getAccessKey()); + Assert.assertEquals(secretKey, configuredCredentials.getSecretKey()); + } +} diff --git a/spring-integration-aws/src/test/java/org/springframework/integration/aws/core/AbstractAWSClientFactoryTests.java b/spring-integration-aws/src/test/java/org/springframework/integration/aws/core/AbstractAWSClientFactoryTests.java new file mode 100644 index 0000000..1db3983 --- /dev/null +++ b/spring-integration-aws/src/test/java/org/springframework/integration/aws/core/AbstractAWSClientFactoryTests.java @@ -0,0 +1,156 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.core; + +import java.net.URI; +import java.util.Map; + +import junit.framework.Assert; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.integration.test.util.TestUtils; + +import com.amazonaws.AmazonWebServiceClient; + +/** + * The abstract test class for Amazon WebService client factory tests + * @author Amol Nayak + * + * @since 1.0 + * + */ +public abstract class AbstractAWSClientFactoryTests { + + protected AbstractAWSClientFactory factory; + + @Before + public final void setup() { + factory = getFactory(); + } + + /** + * The subclass is responsible for returning the appropriate factory implementation + * @return + */ + protected abstract AbstractAWSClientFactory getFactory(); + + /** + * Gets the endpoint for the service in US-EAST-1 region + * @return + */ + protected abstract String getUSEast1Endpoint(); + + + /** + * Gets the endpoint for the service in US-EAST-1 region + * @return + */ + protected abstract String getEUWest1Endpoint(); + + /** + * Gets the Suffix for the endpoint URL which is service specific + * @return + */ + protected abstract String getSuffix(); + + /** + * The test case for giving a US east endpoint without protocol + */ + @Test + public void withUSEast1EndpointWithoutProtocol() { + String usEast1 = getUSEast1Endpoint(); + factory.clear(); + T client = factory.getClient(usEast1 + getSuffix()); + Map map = factory.getClientMap(); + Assert.assertNotNull(map); + Assert.assertEquals(1, map.size()); + Assert.assertTrue("Expected one key with value https://" + usEast1, map.containsKey("https://" + usEast1)); + URI endpoint = TestUtils.getPropertyValue(client, "endpoint", URI.class); + Assert.assertNotNull(endpoint); + Assert.assertEquals(usEast1,endpoint.getHost()); + Assert.assertEquals("https",endpoint.getScheme()); + } + + /** + * Tests the factory by providing an endpoint in US east with protocol as http + */ + @Test + public void withUSEast1EndpointWithProtocol() { + String usEast1 = getUSEast1Endpoint(); + factory.clear(); + T client = factory.getClient("http://" + usEast1 + getSuffix()); + Map map = factory.getClientMap(); + Assert.assertNotNull(map); + Assert.assertEquals(1, map.size()); + Assert.assertTrue("Expected one key with value http://" + usEast1, map.containsKey("http://" + usEast1)); + URI endpoint = TestUtils.getPropertyValue(client, "endpoint", URI.class); + Assert.assertNotNull(endpoint); + Assert.assertEquals(usEast1,endpoint.getHost()); + Assert.assertEquals("http",endpoint.getScheme()); + } + + + /** + * Calls the getClient multiple times to get the same client instance on each invocation + */ + @Test + public void withMultipleCallsToSameEndpoint() { + String usEast1 = getUSEast1Endpoint(); + factory.clear(); + T client = factory.getClient("https://" + usEast1 + getSuffix()); + Map map = factory.getClientMap(); + Assert.assertNotNull(map); + Assert.assertEquals(1, map.size()); + Assert.assertTrue("Expected one key with value http://" + usEast1, map.containsKey("https://" + usEast1)); + //default to https + T client1 = factory.getClient(usEast1 + getSuffix()); + map = factory.getClientMap(); + Assert.assertEquals(1, map.size()); + Assert.assertTrue("Expected one key with value http://" + usEast1, map.containsKey("https://" + usEast1)); + Assert.assertTrue("Expecting to get the same instance of the client, but was not", client == client1); + } + + + /** + *Calls to different endpoints on the same client, expected to return a client with + *appropriate endpoint URI set + */ + @Test + public void withMultipleCallsToDifferentEndpoints() { + String usEast1 = getUSEast1Endpoint(); + String euWest1 = getEUWest1Endpoint(); + factory.clear(); + T client = factory.getClient(usEast1 + getSuffix()); + Map map = factory.getClientMap(); + Assert.assertNotNull(map); + Assert.assertEquals(1, map.size()); + Assert.assertTrue("Expected one key with value https://" + usEast1, map.containsKey("https://" + usEast1)); + URI endpoint = TestUtils.getPropertyValue(client, "endpoint", URI.class); + Assert.assertNotNull(endpoint); + Assert.assertEquals(usEast1,endpoint.getHost()); + Assert.assertEquals("https",endpoint.getScheme()); + client = factory.getClient(euWest1 + getSuffix()); + map = factory.getClientMap(); + Assert.assertNotNull(map); + Assert.assertEquals(2, map.size()); + Assert.assertTrue("Expected one key with value https://" + euWest1, map.containsKey("https://" + euWest1)); + endpoint = TestUtils.getPropertyValue(client, "endpoint", URI.class); + Assert.assertNotNull(endpoint); + Assert.assertEquals(euWest1,endpoint.getHost()); + Assert.assertEquals("https",endpoint.getScheme()); + } +} diff --git a/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/AmazonSESMessageHandlerTests.java b/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/AmazonSESMessageHandlerTests.java new file mode 100644 index 0000000..8b3038b --- /dev/null +++ b/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/AmazonSESMessageHandlerTests.java @@ -0,0 +1,219 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.ses; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.springframework.integration.mail.MailHeaders.BCC; +import static org.springframework.integration.mail.MailHeaders.CC; +import static org.springframework.integration.mail.MailHeaders.FROM; +import static org.springframework.integration.mail.MailHeaders.SUBJECT; +import static org.springframework.integration.mail.MailHeaders.TO; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.mail.internet.MimeMessage; + +import junit.framework.Assert; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.springframework.integration.Message; +import org.springframework.integration.MessageHandlingException; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; + + +/** + * The test class for {@link AmazonSESMessageHandler} + * + * @author Amol Nayak + * + * @since 1.0 + * + */ +public class AmazonSESMessageHandlerTests { + + private static final List messages = new ArrayList(); + private static final List mimeMessages = new ArrayList(); + private static AmazonSESMessageHandler handler; + + @BeforeClass + public static void setupAmazonSESMailSender() { + JavaMailSender sender = mock(JavaMailSender.class); + doAnswer(new Answer() { + public Void answer(InvocationOnMock invocation) throws Throwable { + Object[] args = invocation.getArguments(); + if(args != null) { + messages.add((SimpleMailMessage)args[0]); + } + return null; + } + }).when(sender).send(any(SimpleMailMessage.class)); + + doAnswer(new Answer() { + public Void answer(InvocationOnMock invocation) throws Throwable { + Object[] args = invocation.getArguments(); + if(args != null) { + messages.addAll(Arrays.asList((SimpleMailMessage[])args)); + } + return null; + } + }).when(sender).send(any(SimpleMailMessage[].class)); + + doAnswer(new Answer() { + public Void answer(InvocationOnMock invocation) throws Throwable { + Object[] args = invocation.getArguments(); + if(args != null) { + mimeMessages.addAll(Arrays.asList((MimeMessage[])args)); + } + return null; + } + }).when(sender).send(any(MimeMessage[].class)); + + doAnswer(new Answer() { + public Void answer(InvocationOnMock invocation) throws Throwable { + Object[] args = invocation.getArguments(); + if(args != null) { + mimeMessages.add((MimeMessage)args[0]); + } + return null; + } + }).when(sender).send(any(MimeMessage.class)); + handler = new AmazonSESMessageHandler(sender); + } + + public void clear() { + messages.clear(); + mimeMessages.clear(); + } + + /** + *Test case for sending a message with subject line of unexpected type + *{@link AmazonSESSimpleMailMessage} + * + */ + @Test(expected=MessageHandlingException.class) + public void withIncorrectSubjectClass() { + Message testMessage = MessageBuilder + .withPayload("Test") + .setHeader(SUBJECT, Arrays.asList("Some Header")) + .build(); + handler.handleMessage(testMessage); + } + + + /** + * Test case where the message text used of incorrect type + * + */ + @Test(expected=MessageHandlingException.class) + public void withIncorrectMessageTextClass() { + Message testMessage = MessageBuilder + .withPayload(Arrays.asList("Content")) + .build(); + handler.handleMessage(testMessage); + } + + //Test case to check if from id exists or not cant work now as MailSendingMessageHandler + //doesn't check if from is present or not + + /** + * With all the details + */ + @Test + public void withAllTheDetails() { + clear(); + Message testMessage = MessageBuilder + .withPayload("Test Content") + .setHeader(TO, "to@to.com") + .setHeader(BCC, "bcc@bcc.com") + .setHeader(CC, "cc@cc.com") + .setHeader(SUBJECT, "Test Subject") + .setHeader(FROM, "from@from.com") + .build(); + handler.handleMessage(testMessage); + SimpleMailMessage message = messages.get(0); + String[] to = message.getTo(); + Assert.assertNotNull(to); + Assert.assertEquals(1,to.length); + Assert.assertEquals("to@to.com", to[0]); + + String[] cc = message.getCc(); + Assert.assertNotNull(cc); + Assert.assertEquals(1,cc.length); + Assert.assertEquals("cc@cc.com", cc[0]); + + String[] bcc = message.getBcc(); + Assert.assertNotNull(bcc); + Assert.assertEquals(1,bcc.length); + Assert.assertEquals("bcc@bcc.com", bcc[0]); + + Assert.assertEquals("from@from.com",message.getFrom()); + Assert.assertEquals("Test Subject",message.getSubject()); + Assert.assertEquals("Test Content", message.getText()); + } + +//Might need to uncomment this test later when we start allowing null in TO email id of +//spring-int-mail +// /** +// * +// */ +// @Test +// public void withAllMultipleToBccAndCC() { +// clear(); +// Message testMessage = MessageBuilder +// .withPayload("Test Content") +// .setHeader(TO, +// Arrays.asList("to1@to.com","to2@to.com")) +// .setHeader(BCC, +// Arrays.asList("bcc1@bcc.com","bcc2@bcc.com")) +// .setHeader(CC, +// Arrays.asList("cc1@cc.com","cc2@cc.com")) +// .setHeader(SUBJECT, "Test Subject") +// .setHeader(FROM, "from@from.com") +// .build(); +// handler.handleMessage(testMessage); +// SimpleMailMessage message = messages.get(0); +// String[] to = message.getTo(); +// Assert.assertNotNull(to); +// Assert.assertEquals(2,to.length); +// Assert.assertEquals("to1@to.com", to[0]); +// Assert.assertEquals("to2@to.com", to[1]); +// +// String[] cc = message.getCc(); +// Assert.assertNotNull(cc); +// Assert.assertEquals(2,cc.length); +// Assert.assertEquals("cc1@cc.com", cc[0]); +// Assert.assertEquals("cc2@cc.com", cc[1]); +// +// String[] bcc = message.getBcc(); +// Assert.assertEquals(2,bcc.length); +// Assert.assertEquals("bcc1@bcc.com", bcc[0]); +// Assert.assertEquals("bcc2@bcc.com", bcc[1]); +// +// Assert.assertEquals("from@from.com",message.getFrom()); +// +// Assert.assertEquals("Test Subject",message.getSubject()); +// Assert.assertEquals("Test Content", message.getText()); +// } +} diff --git a/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/config/xml/AmazonSESOutboundAdapterParserTests.java b/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/config/xml/AmazonSESOutboundAdapterParserTests.java new file mode 100644 index 0000000..ce60ac1 --- /dev/null +++ b/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/config/xml/AmazonSESOutboundAdapterParserTests.java @@ -0,0 +1,96 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.ses.config.xml; + +import junit.framework.Assert; + +import org.junit.Test; +import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.aws.core.AWSCredentials; +import org.springframework.integration.core.MessageHandler; +import org.springframework.integration.endpoint.EventDrivenConsumer; +import org.springframework.integration.test.util.TestUtils; + +/** + * The test class for the AmazonSESOutboundAdapterParser + * + * @author Amol Nayak + * + * @since 1.0 + * + */ +public class AmazonSESOutboundAdapterParserTests { + + + /** + * Tests the creation of context with a valid definition by specifying the credentials + * in a properties file + * + */ + @Test + public void propFileValidOutboundAdapter() { + ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:ses-propfile-valid-test.xml"); + EventDrivenConsumer consumer = ctx.getBean("validDefinition",EventDrivenConsumer.class); + assertValidDefinition(consumer); + ctx.close(); + } + + /** + * Tests the creation of context with a valid definition by specifying the credentials + * as individual attributes of the xml + * + */ + @Test + public void propsValidOutboundAdapter() { + ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:ses-props-valid-test.xml"); + EventDrivenConsumer consumer = ctx.getBean("validDefinition",EventDrivenConsumer.class); + assertValidDefinition(consumer); + ctx.close(); + } + + /** + * Tests the creation of context with a valid definition by specifying the credentials + * as individual attributes of the xml + * + */ + @Test + public void refValidOutboundAdapter() { + ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:ses-cred-ref-valid-test.xml"); + EventDrivenConsumer consumer = ctx.getBean("validDefinition",EventDrivenConsumer.class); + assertValidDefinition(consumer); + ctx.close(); + } + + private void assertValidDefinition(EventDrivenConsumer consumer) { + Assert.assertNotNull("Expected a non null EventDrivenConsumer", consumer); + MessageHandler handler = TestUtils.getPropertyValue(consumer, "handler", MessageHandler.class); + Assert.assertNotNull("Expected a non null messagehandler", handler); + AWSCredentials credentials = TestUtils.getPropertyValue(handler, "mailSender.credentials", AWSCredentials.class); + Assert.assertNotNull("Expected a non null instance of credentials", credentials); + Assert.assertEquals("dummy", credentials.getAccessKey()); + Assert.assertEquals("dummy", credentials.getSecretKey()); + } + + /** + * A Test case that tests by specifying both the aws credentials properties file + * and the individual properties to set the credentials + */ + @Test(expected=BeanDefinitionParsingException.class) + public void invalidDefinitionWithBothPropsAndPropFile() { + new ClassPathXmlApplicationContext("classpath:ses-both-awscred-props-property.xml"); + } +} diff --git a/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/core/DefaultAmazonSESMailSenderAWSTests.java b/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/core/DefaultAmazonSESMailSenderAWSTests.java new file mode 100644 index 0000000..95c703b --- /dev/null +++ b/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/core/DefaultAmazonSESMailSenderAWSTests.java @@ -0,0 +1,132 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.aws.ses.core; + +import javax.mail.Session; +import javax.mail.internet.MimeMessage; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.integration.aws.core.PropertiesAWSCredentials; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.MimeMessageHelper; + +/** +* +* The test class for the {@link DefaultAmazonSESMailSender} class. +* +* NOTE: You will have to modify the to and from email's yourselves +* as you can send to verified emails only as part of the free tier +* in which you may be running the test. +* To run this test you need to have your AWSAccess key and Secret key in the +* file awscredentials.properties in the classpath. This file is not present in the +* repository and you need to add one yourselves to src/test/resources folder and have +* two properties accessKey and secretKey in it containing the access and the secret key +* +* @author Amol Nayak +* +* @since 1.0 +* +*/ +public class DefaultAmazonSESMailSenderAWSTests { + + private static final String TO_EMAIL_ID = "amolnayak311@gmail.com"; + private static DefaultAmazonSESMailSender sender; + + + @BeforeClass + public static final void setupSender() throws Exception { + PropertiesAWSCredentials credentials = + new PropertiesAWSCredentials("classpath:awscredentials.properties"); + credentials.afterPropertiesSet(); + sender = new DefaultAmazonSESMailSender(credentials); + } + + /** + * Send a mail using {@link AmazonSESSimpleMailMessage} + */ + @Test + public void sendSimpleMailMessage() { + SimpleMailMessage message = new SimpleMailMessage(); + message.setFrom(TO_EMAIL_ID); + message.setText("Some Test body content"); + message.setSubject("Test subject message"); + message.setTo(new String[]{TO_EMAIL_ID}); + sender.send(message); + } + + + /** + * Send a mail using {@link AmazonSESSimpleMailMessage} + */ + @Test + public void sendSimpleMailMessageArray() { + SimpleMailMessage[] messages = new SimpleMailMessage[2]; + SimpleMailMessage message = new SimpleMailMessage(); + message.setFrom(TO_EMAIL_ID); + message.setText("Some Test body content one"); + message.setSubject("Test subject message one"); + message.setTo(new String[]{TO_EMAIL_ID}); + messages[0] = message; + message = new SimpleMailMessage(); + message.setFrom(TO_EMAIL_ID); + message.setText("Some Test body content two"); + message.setSubject("Test subject message two"); + message.setTo(new String[]{TO_EMAIL_ID}); + messages[1] = message; + sender.send(messages); + } + + + /** + * The test case for sending a {@link MimeMessage} + */ + @Test + public void sendMimeMessage() throws Exception { + Session session = sender.getSession(); + MimeMessageHelper helper = new MimeMessageHelper(new MimeMessage(session)); + helper.setText("Some HTML Text", true); + helper.setTo(TO_EMAIL_ID); + helper.setFrom(TO_EMAIL_ID); + helper.setSubject("Some HTML Message's Subject Line"); + MimeMessage message = helper.getMimeMessage(); + sender.send(message); + } + + /** + * The test case for sending a {@link MimeMessage} + */ + @Test + public void sendMimeMessageArray() throws Exception { + Session session = sender.getSession(); + MimeMessage[] messages = new MimeMessage[2]; + MimeMessageHelper helper = new MimeMessageHelper(new MimeMessage(session)); + helper.setText("Some HTML Text One", true); + helper.setTo(TO_EMAIL_ID); + helper.setFrom(TO_EMAIL_ID); + helper.setSubject("Some HTML Message's Subject Line One"); + MimeMessage message = helper.getMimeMessage(); + messages[0] = message; + helper = new MimeMessageHelper(new MimeMessage(session)); + helper.setText("Some HTML Text Two", true); + helper.setTo(TO_EMAIL_ID); + helper.setFrom(TO_EMAIL_ID); + helper.setSubject("Some HTML Message's Subject Line Two"); + message = helper.getMimeMessage(); + messages[1] = message; + sender.send(messages); + } +} diff --git a/spring-integration-aws/src/test/resources/log4j.properties b/spring-integration-aws/src/test/resources/log4j.properties new file mode 100644 index 0000000..8e9bc68 --- /dev/null +++ b/spring-integration-aws/src/test/resources/log4j.properties @@ -0,0 +1,8 @@ +log4j.rootCategory=INFO, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n + +log4j.category.org.springframework=WARN +log4j.category.org.springframework.integration.aws=DEBUG diff --git a/spring-integration-aws/src/test/resources/ses-both-awscred-props-property.xml b/spring-integration-aws/src/test/resources/ses-both-awscred-props-property.xml new file mode 100644 index 0000000..2925b23 --- /dev/null +++ b/spring-integration-aws/src/test/resources/ses-both-awscred-props-property.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/spring-integration-aws/src/test/resources/ses-cred-ref-valid-test.xml b/spring-integration-aws/src/test/resources/ses-cred-ref-valid-test.xml new file mode 100644 index 0000000..6654035 --- /dev/null +++ b/spring-integration-aws/src/test/resources/ses-cred-ref-valid-test.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + diff --git a/spring-integration-aws/src/test/resources/ses-propfile-valid-test.xml b/spring-integration-aws/src/test/resources/ses-propfile-valid-test.xml new file mode 100644 index 0000000..acab1b4 --- /dev/null +++ b/spring-integration-aws/src/test/resources/ses-propfile-valid-test.xml @@ -0,0 +1,15 @@ + + + + + + + diff --git a/spring-integration-aws/src/test/resources/ses-props-valid-test.xml b/spring-integration-aws/src/test/resources/ses-props-valid-test.xml new file mode 100644 index 0000000..0fa92ae --- /dev/null +++ b/spring-integration-aws/src/test/resources/ses-props-valid-test.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/spring-integration-aws/src/test/resources/testawscredentials.properties b/spring-integration-aws/src/test/resources/testawscredentials.properties new file mode 100644 index 0000000..89f7ecb --- /dev/null +++ b/spring-integration-aws/src/test/resources/testawscredentials.properties @@ -0,0 +1,2 @@ +accessKey=dummy +secretKey=dummy \ No newline at end of file From 897fc0e9b402ef4cf7729dece281580c8aa7784b Mon Sep 17 00:00:00 2001 From: Gunnar Hillert Date: Thu, 17 Jan 2013 17:14:54 -0500 Subject: [PATCH 3/3] INTEXT-6 - Add AWS SES Samples * Add SES JavaMailSender Sample * Add SES Spring Integration Sample * Refactor DefaultAmazonSESMailSender to implement JavaMailSender and delegate to JavaMailSenderImpl rather than extending it * Cleanup --- samples/mail-ses-integration/README.md | 4 + samples/mail-ses-integration/pom.xml | 84 ++++ .../samples/mailses/EmailService.java | 24 ++ .../integration/samples/mailses/Main.java | 101 +++++ .../spring-integration-context.xml | 34 ++ .../src/main/resources/log4j.xml | 28 ++ samples/mail-ses/README.md | 5 + samples/mail-ses/pom.xml | 84 ++++ .../integration/samples/mailses/Main.java | 165 ++++++++ .../spring-integration-context.xml | 44 +++ samples/mail-ses/src/main/resources/log4j.xml | 28 ++ spring-integration-aws/README.md | 47 +-- spring-integration-aws/pom.xml | 374 +++++++++--------- .../aws/config/xml/AWSNamespaceHandler.java | 2 +- ...stractAWSOutboundChannelAdapterParser.java | 2 +- .../aws/config/xml/AmazonWSParserUtils.java | 2 +- .../aws/core/AWSClientFactory.java | 2 +- .../integration/aws/core/AWSCredentials.java | 2 +- .../aws/core/AWSOperationException.java | 2 +- .../aws/core/AbstractAWSClientFactory.java | 2 +- .../aws/core/BasicAWSCredentials.java | 17 +- .../core/InvalidAWSCredentialsException.java | 2 +- .../aws/core/PropertiesAWSCredentials.java | 15 +- .../aws/ses/AmazonSESMessageHandler.java | 2 +- .../xml/AmazonSESOutboundAdapterParser.java | 2 +- .../ses/core/AmazonSESMailSendException.java | 2 +- .../ses/core/DefaultAmazonSESMailSender.java | 127 +++--- .../aws/common/AWSClientFactoryTest.java | 2 +- .../integration/aws/common/BaseTestCase.java | 2 +- ...tAWSOutboundChannelAdapterParserTests.java | 2 +- .../core/AbstractAWSClientFactoryTests.java | 2 +- .../aws/ses/AmazonSESMessageHandlerTests.java | 2 +- .../AmazonSESOutboundAdapterParserTests.java | 16 +- .../DefaultAmazonSESMailSenderAWSTests.java | 11 +- 34 files changed, 929 insertions(+), 311 deletions(-) create mode 100644 samples/mail-ses-integration/README.md create mode 100644 samples/mail-ses-integration/pom.xml create mode 100644 samples/mail-ses-integration/src/main/java/org/springframework/integration/samples/mailses/EmailService.java create mode 100644 samples/mail-ses-integration/src/main/java/org/springframework/integration/samples/mailses/Main.java create mode 100644 samples/mail-ses-integration/src/main/resources/META-INF/spring/integration/spring-integration-context.xml create mode 100644 samples/mail-ses-integration/src/main/resources/log4j.xml create mode 100644 samples/mail-ses/README.md create mode 100644 samples/mail-ses/pom.xml create mode 100644 samples/mail-ses/src/main/java/org/springframework/integration/samples/mailses/Main.java create mode 100644 samples/mail-ses/src/main/resources/META-INF/spring/integration/spring-integration-context.xml create mode 100644 samples/mail-ses/src/main/resources/log4j.xml diff --git a/samples/mail-ses-integration/README.md b/samples/mail-ses-integration/README.md new file mode 100644 index 0000000..667fb78 --- /dev/null +++ b/samples/mail-ses-integration/README.md @@ -0,0 +1,4 @@ +AWS SES Spring Integration Sample +========================= + + diff --git a/samples/mail-ses-integration/pom.xml b/samples/mail-ses-integration/pom.xml new file mode 100644 index 0000000..56cb721 --- /dev/null +++ b/samples/mail-ses-integration/pom.xml @@ -0,0 +1,84 @@ + + 4.0.0 + org.springframework.integration.samples + mail-ses-integration + AWS SES Mail Demo + 1.0.0.BUILD-SNAPSHOT + + + 2.2.1 + + + + UTF-8 + 2.2.0.RELEASE + 1.2.17 + 4.10 + + + + org.springframework.integration + spring-integration-mail + ${spring.integration.version} + + + org.springframework.integration + spring-integration-test + ${spring.integration.version} + + + org.springframework.integration + spring-integration-aws + 0.5.0.BUILD-SNAPSHOT + + + log4j + log4j + ${log4j.version} + + + + junit + junit + ${junit.version} + test + + + org.subethamail + subethasmtp-wiser + 1.2 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.5.1 + + 1.6 + 1.6 + -Xlint:all + true + true + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + org.springframework.integration.samples.mailses.Main + + + + + + + repo.springsource.org.milestone + SpringSource Maven Milestone Repository + https://repo.springsource.org/libs-milestone + + + \ No newline at end of file diff --git a/samples/mail-ses-integration/src/main/java/org/springframework/integration/samples/mailses/EmailService.java b/samples/mail-ses-integration/src/main/java/org/springframework/integration/samples/mailses/EmailService.java new file mode 100644 index 0000000..a3a4050 --- /dev/null +++ b/samples/mail-ses-integration/src/main/java/org/springframework/integration/samples/mailses/EmailService.java @@ -0,0 +1,24 @@ +package org.springframework.integration.samples.mailses; + +import org.springframework.integration.annotation.Header; +import org.springframework.integration.annotation.Payload; +import org.springframework.integration.mail.MailHeaders; + +public interface EmailService { + + + void send( + + @Header(MailHeaders.FROM) + String fromEmail, + + @Header(MailHeaders.TO) + String toEmail, + + @Header(MailHeaders.SUBJECT) + String subject, + + @Payload + String body); + +} diff --git a/samples/mail-ses-integration/src/main/java/org/springframework/integration/samples/mailses/Main.java b/samples/mail-ses-integration/src/main/java/org/springframework/integration/samples/mailses/Main.java new file mode 100644 index 0000000..7972880 --- /dev/null +++ b/samples/mail-ses-integration/src/main/java/org/springframework/integration/samples/mailses/Main.java @@ -0,0 +1,101 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.samples.mailses; + +import java.util.Scanner; + +import org.apache.log4j.Logger; +import org.springframework.context.support.GenericXmlApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; + +/** + * Starts the Spring Context and will initialize the Spring Integration routes. + * + * @author Gunnar Hillert + * @since 1.0 + * + */ +public final class Main { + + private static final Logger LOGGER = Logger.getLogger(Main.class); + + private static final String HORIZONTAL_LINE = "\n========================================================="; + + private Main() { + } + + /** + * Load the Spring Integration Application Context + * + * @param args - command line arguments + */ + public static void main(final String... args) { + + final Scanner scanner = new Scanner(System.in); + + LOGGER.info(HORIZONTAL_LINE + "\n" + + "\n Welcome to Spring Integration! " + + "\n" + + "\n For more information please visit: " + + "\n http://www.springsource.org/spring-integration " + + "\n" + HORIZONTAL_LINE); + + final GenericXmlApplicationContext context = new GenericXmlApplicationContext(); + final ConfigurableEnvironment environment = context.getEnvironment(); + + final String fromEmailAddress; + final String toEmailAddress; + final String subject; + final String body; + + System.out.print("\nFrom which email address would you like to send a message?: "); + fromEmailAddress = scanner.nextLine(); + + System.out.print("To which email address would you like to send a message?: "); + toEmailAddress = scanner.nextLine(); + + System.out.print("What is the subject line?: "); + subject = scanner.nextLine(); + + System.out.print("What is the body of the message?: "); + body = scanner.nextLine(); + + if (!environment.containsProperty("accessKey")) { + System.out.print("Please enter your access key: "); + final String accessKey = scanner.nextLine(); + environment.getSystemProperties().put("accessKey", accessKey); + } + + if (!environment.containsProperty("secretKey")) { + System.out.print("Please enter your secret key: "); + final String secretKey = scanner.nextLine(); + environment.getSystemProperties().put("secretKey", secretKey); + } + + context.load("classpath:META-INF/spring/integration/*-context.xml"); + context.registerShutdownHook(); + context.refresh(); + + final EmailService emailService = context.getBean(EmailService.class); + + emailService.send(fromEmailAddress, toEmailAddress, subject, body); + + System.out.println(String.format("The email to '%s' was sent successfully.", toEmailAddress)); + + System.exit(0); + + } +} diff --git a/samples/mail-ses-integration/src/main/resources/META-INF/spring/integration/spring-integration-context.xml b/samples/mail-ses-integration/src/main/resources/META-INF/spring/integration/spring-integration-context.xml new file mode 100644 index 0000000..81623df --- /dev/null +++ b/samples/mail-ses-integration/src/main/resources/META-INF/spring/integration/spring-integration-context.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/samples/mail-ses-integration/src/main/resources/log4j.xml b/samples/mail-ses-integration/src/main/resources/log4j.xml new file mode 100644 index 0000000..364cd81 --- /dev/null +++ b/samples/mail-ses-integration/src/main/resources/log4j.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/mail-ses/README.md b/samples/mail-ses/README.md new file mode 100644 index 0000000..35a1138 --- /dev/null +++ b/samples/mail-ses/README.md @@ -0,0 +1,5 @@ +AWS SES MailSender sample +========================= + +This sample uses a Spring-provided *JavaMailSender* to send emails. This application demonstrates that by only replacing the *JavaMailSender* XML bean declaration with the *DefaultAmazonSESMailSender*, existing applications can send emails using Amazon SES without changing application code. The *DefaultAmazonSESMailSender* is provided by the *Spring Integration Extensions AWS Module*. + diff --git a/samples/mail-ses/pom.xml b/samples/mail-ses/pom.xml new file mode 100644 index 0000000..a0eae15 --- /dev/null +++ b/samples/mail-ses/pom.xml @@ -0,0 +1,84 @@ + + 4.0.0 + org.springframework.integration.samples + mail-ses + AWS SES Mail Demo + 1.0.0.BUILD-SNAPSHOT + + + 2.2.1 + + + + UTF-8 + 2.2.0.RELEASE + 1.2.17 + 4.10 + + + + org.springframework.integration + spring-integration-mail + ${spring.integration.version} + + + org.springframework.integration + spring-integration-test + ${spring.integration.version} + + + org.springframework.integration + spring-integration-aws + 0.5.0.BUILD-SNAPSHOT + + + log4j + log4j + ${log4j.version} + + + + junit + junit + ${junit.version} + test + + + org.subethamail + subethasmtp-wiser + 1.2 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.5.1 + + 1.6 + 1.6 + -Xlint:all + true + true + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + org.springframework.integration.samples.mailses.Main + + + + + + + repo.springsource.org.milestone + SpringSource Maven Milestone Repository + https://repo.springsource.org/libs-milestone + + + \ No newline at end of file diff --git a/samples/mail-ses/src/main/java/org/springframework/integration/samples/mailses/Main.java b/samples/mail-ses/src/main/java/org/springframework/integration/samples/mailses/Main.java new file mode 100644 index 0000000..2712acf --- /dev/null +++ b/samples/mail-ses/src/main/java/org/springframework/integration/samples/mailses/Main.java @@ -0,0 +1,165 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.springframework.integration.samples.mailses; + +import java.util.List; +import java.util.Scanner; + +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; + +import org.apache.log4j.Logger; +import org.springframework.context.support.GenericXmlApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.mail.MailException; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessagePreparator; +import org.subethamail.wiser.Wiser; +import org.subethamail.wiser.WiserMessage; + +/** + * Starts the Spring Context and will initialize the Spring Integration routes. + * + * @author Gunnar Hillert + * @since 2.2 + * + */ +public final class Main { + + private static final Logger LOGGER = Logger.getLogger(Main.class); + + private static final String HORIZONTAL_LINE = "\n========================================================="; + + private Main() { + } + + /** + * Load the Spring Integration Application Context + * + * @param args - command line arguments + */ + public static void main(final String... args) { + + final Scanner scanner = new Scanner(System.in); + + LOGGER.info(HORIZONTAL_LINE + "\n" + + "\n Welcome to Spring Integration! " + + "\n" + + "\n For more information please visit: " + + "\n http://www.springsource.org/spring-integration " + + "\n" + HORIZONTAL_LINE); + + System.out.println("Please enter a choice and press : "); + System.out.println("\t1. Use Embedded SMTP Server (Wiser)"); + System.out.println("\t2. Use Amazon SES"); + + System.out.println("\tq. Quit the application"); + System.out.print("Enter your choice: "); + + final GenericXmlApplicationContext context = new GenericXmlApplicationContext(); + final ConfigurableEnvironment environment = context.getEnvironment(); + + boolean usingWiser = false; + + String toEmailAddress; + + while (true) { + final String input = scanner.nextLine(); + + if("1".equals(input.trim())) { + environment.setActiveProfiles("default"); + usingWiser = true; + + System.out.print("\nTo which email address would you like to send a message?: "); + toEmailAddress = scanner.nextLine(); + + break; + } else if("2".equals(input.trim())) { + environment.setActiveProfiles("aws"); + + if (!environment.containsProperty("accessKey")) { + System.out.print("\nPlease enter your access key: "); + final String accessKey = scanner.nextLine(); + environment.getSystemProperties().put("accessKey", accessKey); + } + + if (!environment.containsProperty("secretKey")) { + System.out.print("\nPlease enter your secret key: "); + final String secretKey = scanner.nextLine(); + environment.getSystemProperties().put("secretKey", secretKey); + } + System.out.print("\nTo which email address would you like to send a message?: "); + toEmailAddress = scanner.nextLine(); + + break; + } else if("q".equals(input.trim())) { + System.out.println("Exiting application...bye."); + System.exit(0); + } else { + System.out.println("Invalid choice\n\n"); + System.out.print("Enter you choice: "); + } + } + + context.load("classpath:META-INF/spring/integration/*-context.xml"); + context.registerShutdownHook(); + context.refresh(); + + final JavaMailSender ms = context.getBean(JavaMailSender.class); + final String toEmailAddressToUse = toEmailAddress; + final MimeMessagePreparator preparator = new MimeMessagePreparator() { + + public void prepare(MimeMessage mimeMessage) throws Exception { + + mimeMessage.setRecipient(Message.RecipientType.TO, + new InternetAddress(toEmailAddressToUse)); + mimeMessage.setFrom(new InternetAddress(toEmailAddressToUse)); + mimeMessage.setSubject("Testing Email - Subject"); + mimeMessage.setText("Hello World"); + } + + }; + + try { + ms.send(preparator); + } catch (MailException e) { + throw new IllegalStateException(e); + } + + System.out.println(String.format("The email to '%s' was sent successfully.", toEmailAddress)); + + if (usingWiser) { + Wiser wiser = context.getBean(Wiser.class); + List messages = wiser.getMessages(); + + final String from; + final String subject; + try { + from = messages.get(0).getMimeMessage().getFrom()[0].toString(); + subject = messages.get(0).getMimeMessage().getSubject(); + } catch (MessagingException e) { + throw new IllegalStateException(e); + } + + System.out.println(String.format("Wiser received an email from '%s' with subject '%s'", from, subject)); + } + + System.exit(0); + + } +} diff --git a/samples/mail-ses/src/main/resources/META-INF/spring/integration/spring-integration-context.xml b/samples/mail-ses/src/main/resources/META-INF/spring/integration/spring-integration-context.xml new file mode 100644 index 0000000..5d35a49 --- /dev/null +++ b/samples/mail-ses/src/main/resources/META-INF/spring/integration/spring-integration-context.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/mail-ses/src/main/resources/log4j.xml b/samples/mail-ses/src/main/resources/log4j.xml new file mode 100644 index 0000000..364cd81 --- /dev/null +++ b/samples/mail-ses/src/main/resources/log4j.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-integration-aws/README.md b/spring-integration-aws/README.md index e091601..9ffaec4 100644 --- a/spring-integration-aws/README.md +++ b/spring-integration-aws/README.md @@ -1,18 +1,16 @@ Spring Integration Extension for Amazon Web Services (AWS) ========================================================== +# Introduction +## Amazon Web Services (AWS) -##Small Introduction to Amazon Web Services (AWS) +Launched in 2006, [Amazon Web Services][] (AWS) provides key infrastructure services for business through its cloud computing platform. Using cloud computing businesses can adopt a new business model whereby they do not have to plan and invest in procuring their own IT infrastructure. They can use the infrastructure and services provided by the cloud service provider and pay as they use the services. Visit [http://aws.amazon.com/products/] for more details about various products offered by Amazon as a part their cloud computing services. +*Spring Integration Extension for Amazon Web Services* provides Spring Integration adapters for the various services provided by the [AWS SDK for Java][]. -Launched in 2006, Amazon Web Services (AWS) started providing key infrastructure for business as web services, which, also known as cloud computing. Using cloud computing businesses can adopt a new business model whereby they do not have to plan and invest in procuring their own IT infrastructure. They can use the infrastructure and services provided by the cloud service provider and pay as they use the services. -Visit [http://aws.amazon.com/products/] for more details about various products offered by Amazon as a part their cloud computing services. +## Spring Integration's extensions to AWS -##Introduction to Spring Integration's extensions to AWS - -This guide intends to explain in brief about various adapters available for the AWS and sample xml tag definition for each of them and code snippets wherever necessary, - -The library aims to provide adapters for following AWS Services +This guide intends to explain briefly the various adapters available for [Amazon Web Services][] such as: * **Amazon Simple Email Service (SES)** * **Amazon Simple Storage Service (S3)** (Development complete, coming soon) @@ -21,35 +19,27 @@ The library aims to provide adapters for following AWS Services * **Amazon SimpleDB** (Not initiated) * **Amazon SNS** (Not initiated) -Of the above libraries, SES and SNS have outbound adapters only. All other -services have inbound and outbound adapters. The SQS inbound adapter is capable of receiving notifications sent out from SNS where the topic is an SQS Queue. -For DymamoDB and SimpleDB, apart from inbound and outbound adapters we shall provide a *MessageStore* implementation too. +Sample XML Namespace configurations for each adapter as well as sample code snippets are provided wherever necessary. Of the above libraries, *SES* and *SNS* provide outbound adapters only. All other services have inbound and outbound adapters. The *SQS* inbound adapter is capable of receiving notifications sent out from *SNS* where the topic is an *SQS* Queue. +For *DymamoDB* and *SimpleDB*, besides providing *Inbound*- and *Outbound Adapters*, a *MessageStore* implementation is provided, too. -###Executing the test cases. +# Executing the test cases. -All the test cases for the adapters are present in the *src/test/java* folder. On executing the build, maven's surefire plugin will execute all -the tests. Please note that all the tests ending with **AWSTests.java* connect -to the actual webservices and are excluded by default in the maven build. -All other tests rely on mocking to test the functionality. You need to execute the **AWSTests.java* -explicitly, manually to test the conectivity to AWS using your credentials. -All these **AWSTests.java* tests look for the file *awscredentials.properties* in the classpath. -To be on the safer side create one at location *src/test/resources* as this file -*spring-integration-aws/src/test/resources/awscredentials.properties* is added to *.gitignore* file. -This will prevent this file to be checked in accidently and revealing your credentials. -This file needs to have two properties *accessKey* and *secretKey* holding the values of your access key and secret key respectively. -> **Note: AWS Services are chargeable and we recommend not to execute the **AWSTests.java* as part of your regular builds. -AWS does provide a free tier which is sufficient to perform your tests without being -charged (not true for DynamoDB though), however keep a check on your account usage regularly. -Get more information about AWS free tier at [http://aws.amazon.com/free/][]** +All test cases for the adapters are present in the *src/test/java* folder. On executing the build, maven's surefire plugin will execute all the tests. +> Please note that all the tests ending with **AWSTests.java* connect to the actual [Amazon Web Services][] and are excluded by default in the maven build. All other tests rely on mocking to test the functionality. You need to execute the **AWSTests.java* manually to test the connectivity to AWS using your credentials. +All these **AWSTests.java* tests look for the file *awscredentials.properties* in the classpath. To be on the safe side, create the following file at *src/test/resources*: +*spring-integration-aws/src/test/resources/awscredentials.properties*. It is added to the *.gitignore* file by default. This will prevent this file to be checked in accidentally and revealing your credentials. + +This file needs to have two properties *accessKey* and *secretKey*, holding the values of your access key and secret key respectively. + +> **Note: AWS Services are chargeable and we recommend not to execute the **AWSTests.java* as part of your regular builds. AWS does provide a free tier which is sufficient to perform your tests without being charged (not true for DynamoDB though), however keep a check on your account usage regularly. Get more information about AWS free tier at [http://aws.amazon.com/free/][]** #Adapters ##Simple Email Service (SES) - ###Introduction Amazon Simple Email Service (SES) is a web service for sending emails from the cloud. It supports two types of mails currently, the simple mail and raw email. @@ -134,7 +124,8 @@ We shall now see a java code snippet to send a mail using the SNS adapter The messages over this channel are consumed by the outbound SES adapter and the mail is sent out using SES. - +[AWS SDK for Java]: http://aws.amazon.com/sdkforjava/ +[Amazon Web Services]: http://aws.amazon.com/ [http://aws.amazon.com/products/]: http://aws.amazon.com/products/ [http://aws.amazon.com/ses/]: http://aws.amazon.com/ses/ [http://aws.amazon.com/documentation/ses/]: http://aws.amazon.com/documentation/ses/ diff --git a/spring-integration-aws/pom.xml b/spring-integration-aws/pom.xml index b868095..6e58330 100644 --- a/spring-integration-aws/pom.xml +++ b/spring-integration-aws/pom.xml @@ -1,28 +1,29 @@ - - 4.0.0 - org.springframework.integration - spring-integration-aws - 1.0.0.BUILD-SNAPSHOT - Spring Integration Amazon Web Services Support - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - repo - - + + 4.0.0 + org.springframework.integration + spring-integration-aws + 0.5.0.BUILD-SNAPSHOT + Spring Integration Amazon Web Services Support + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + 2.2.1 - 2.2.0.RELEASE - 3.1.3.RELEASE - 1.3.12 - 1.5 + 2.2.0.RELEASE + 3.1.3.RELEASE + 1.3.12 + 1.5 1.1.1 2.0.1 1.4.4 @@ -34,190 +35,189 @@ UTF8 - - - - src/main/java - - **/* - - - **/*.java - - - - src/main/resources - - **/* - - - - - - src/test/java - - **/* - - - **/*.java - - - - src/test/resources - - **/* - - - - - - maven-compiler-plugin - - 1.6 - 1.6 - - - - maven-surefire-plugin - - - **/*Tests.java - - - **/*Abstract*.java - **/*AWSTests.java - - - - - - - - springsource-libs-milestone - Spring Framework Maven Milestone Repository - https://repo.springsource.org/libs-milestone - - - - - com.amazonaws - aws-java-sdk - ${aws.sdk.version} - compile - + + + + src/main/java + + **/* + + + **/*.java + + + + src/main/resources + + **/* + + + + + + src/test/java + + **/* + + + **/*.java + + + + src/test/resources + + **/* + + + + + + maven-compiler-plugin + + 1.6 + 1.6 + + + + maven-surefire-plugin + + + **/*Tests.java + + + **/*Abstract*.java + **/*AWSTests.java + + + + + + + + springsource-libs-milestone + Spring Framework Maven Milestone Repository + https://repo.springsource.org/libs-milestone + + + + + com.amazonaws + aws-java-sdk + ${aws.sdk.version} + compile + - - commons-codec - commons-codec - ${commons.codec.version} - compile - + + commons-codec + commons-codec + ${commons.codec.version} + compile + - - commons-logging - commons-logging - ${commons.logging.version} - compile - + + commons-logging + commons-logging + ${commons.logging.version} + compile + - - commons-io - commons-io - ${commons.io.version} - compile - + + commons-io + commons-io + ${commons.io.version} + compile + - - javax.mail - mail - ${java.mail.version} - compile - + + javax.mail + mail + ${java.mail.version} + compile + - - org.springframework.integration - spring-integration-core - ${spring.integration.version} - compile - + + org.springframework.integration + spring-integration-core + ${spring.integration.version} + compile + - - org.springframework.integration - spring-integration-mail - ${spring.integration.version} - compile - + + org.springframework.integration + spring-integration-mail + ${spring.integration.version} + provided + + + org.codehaus.jackson + jackson-core-asl + ${jackson.version} + compile + - - org.codehaus.jackson - jackson-core-asl - ${jackson.version} - compile - + + org.codehaus.jackson + jackson-mapper-asl + ${jackson.version} + compile + - - org.codehaus.jackson - jackson-mapper-asl - ${jackson.version} - compile - + + org.apache.httpcomponents + httpclient + ${apache.httpclient.version} + compile + - - org.apache.httpcomponents - httpclient - ${apache.httpclient.version} - compile - + + org.apache.httpcomponents + httpcore + ${apache.httpclient.version} + compile + - - org.apache.httpcomponents - httpcore - ${apache.httpclient.version} - compile - + + org.springframework.integration + spring-integration-test + ${spring.integration.version} + test + - - org.springframework.integration - spring-integration-test - ${spring.integration.version} - test - + + org.hamcrest + hamcrest-core + ${hamcrest.version} + test + - - org.hamcrest - hamcrest-core - ${hamcrest.version} - test - + + junit + junit + ${junit.version} + test + - - junit - junit - ${junit.version} - test - + + org.mockito + mockito-all + ${mockito.version} + test + - - org.mockito - mockito-all - ${mockito.version} - test - + + org.mockito + mockito-core + ${mockito.version} + test + - - org.mockito - mockito-core - ${mockito.version} - test - + + org.springframework + spring-test + ${spring.test.version} + test + - - org.springframework - spring-test - ${spring.test.version} - test - - - + diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AWSNamespaceHandler.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AWSNamespaceHandler.java index 044e702..5894d8c 100644 --- a/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AWSNamespaceHandler.java +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AWSNamespaceHandler.java @@ -23,7 +23,7 @@ import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHa * * @author Amol Nayak * - * @since 1.0 + * @since 0.5 * */ public class AWSNamespaceHandler extends diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AbstractAWSOutboundChannelAdapterParser.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AbstractAWSOutboundChannelAdapterParser.java index 3ea2a57..3a1d848 100644 --- a/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AbstractAWSOutboundChannelAdapterParser.java +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AbstractAWSOutboundChannelAdapterParser.java @@ -29,7 +29,7 @@ import org.w3c.dom.Element; * * @author Amol Nayak * - * @since 1.0 + * @since 0.5 * */ public abstract class AbstractAWSOutboundChannelAdapterParser extends diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AmazonWSParserUtils.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AmazonWSParserUtils.java index 974550e..d8f0f34 100644 --- a/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AmazonWSParserUtils.java +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/config/xml/AmazonWSParserUtils.java @@ -28,7 +28,7 @@ import org.w3c.dom.Element; * * @author Amol Nayak * - * @since 1.0 + * @since 0.5 * */ public final class AmazonWSParserUtils { diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSClientFactory.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSClientFactory.java index 1c74cbe..c76216f 100644 --- a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSClientFactory.java +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSClientFactory.java @@ -23,7 +23,7 @@ import com.amazonaws.AmazonWebServiceClient; * * @author Amol Nayak * - * @since 1.0 + * @since 0.5 * */ public interface AWSClientFactory { diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSCredentials.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSCredentials.java index 664f231..3ae05ef 100644 --- a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSCredentials.java +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSCredentials.java @@ -20,7 +20,7 @@ package org.springframework.integration.aws.core; * * @author Amol Nayak * - * @since 1.0 + * @since 0.5 * */ public interface AWSCredentials { diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSOperationException.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSOperationException.java index 1b9035d..70c2df8 100644 --- a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSOperationException.java +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AWSOperationException.java @@ -20,7 +20,7 @@ package org.springframework.integration.aws.core; * * @author Amol Nayak * - * @since 1.0 + * @since 0.5 * */ public class AWSOperationException extends RuntimeException { diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AbstractAWSClientFactory.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AbstractAWSClientFactory.java index e630607..6cc42ee 100644 --- a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AbstractAWSClientFactory.java +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/AbstractAWSClientFactory.java @@ -34,7 +34,7 @@ import com.amazonaws.AmazonWebServiceClient; * * @author Amol Nayak * - * @since 1.0 + * @since 0.5 * */ public abstract class AbstractAWSClientFactory implements AWSClientFactory { diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/BasicAWSCredentials.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/BasicAWSCredentials.java index 69cebea..30fe0cb 100644 --- a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/BasicAWSCredentials.java +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/BasicAWSCredentials.java @@ -15,13 +15,15 @@ */ package org.springframework.integration.aws.core; +import org.springframework.util.Assert; + /** * The basic implementation class holding the Access key and the secret * key for the AWS account . * * @author Amol Nayak * - * @since 1.0 + * @since 0.5 * */ public class BasicAWSCredentials implements AWSCredentials { @@ -45,12 +47,15 @@ public class BasicAWSCredentials implements AWSCredentials { } /**. - * The constructor accepting the access and secret key - * @param accessKey - * @param secretKey + * The constructor accepting the access and secret key. + * + * @param accessKey Must not be null or empty + * @param secretKey Must not be null or empty */ public BasicAWSCredentials(String accessKey, String secretKey) { - super(); + Assert.hasText(accessKey, "The accessKey parameter must not be null or empty."); + Assert.hasText(secretKey, "The secretKey parameter must not be null or empty."); + this.accessKey = accessKey; this.secretKey = secretKey; } @@ -68,6 +73,7 @@ public class BasicAWSCredentials implements AWSCredentials { * @param accessKey */ public void setAccessKey(String accessKey) { + Assert.hasText(accessKey, "The accessKey parameter must not be null or empty."); this.accessKey = accessKey; } @@ -84,6 +90,7 @@ public class BasicAWSCredentials implements AWSCredentials { * @param secretKey */ public void setSecretKey(String secretKey) { + Assert.hasText(secretKey, "The secretKey parameter must not be null or empty."); this.secretKey = secretKey; } } diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/InvalidAWSCredentialsException.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/InvalidAWSCredentialsException.java index 408c916..5abde9f 100644 --- a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/InvalidAWSCredentialsException.java +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/InvalidAWSCredentialsException.java @@ -20,7 +20,7 @@ package org.springframework.integration.aws.core; * * @author Amol Nayak * - * @since 1.0 + * @since 0.5 * */ public class InvalidAWSCredentialsException extends RuntimeException { diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/PropertiesAWSCredentials.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/PropertiesAWSCredentials.java index 2f2ef39..567efa2 100644 --- a/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/PropertiesAWSCredentials.java +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/core/PropertiesAWSCredentials.java @@ -21,6 +21,7 @@ import java.util.Properties; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; @@ -30,7 +31,7 @@ import org.springframework.util.StringUtils; * * @author Amol Nayak * - * @since 1.0 + * @since 0.5 * */ public class PropertiesAWSCredentials extends BasicAWSCredentials implements InitializingBean { @@ -44,11 +45,13 @@ public class PropertiesAWSCredentials extends BasicAWSCredentials implements Ini private String propertyFileName; /**. - * Constructor accepting the properties file - * @param propertyFileName + * Constructor accepting the properties file. + * + * @param propertyFileName Must not be null or empty */ public PropertiesAWSCredentials(String propertyFileName) { super(); + Assert.hasText(propertyFileName, "The propertyFileName parameter must not be null or empty."); this.propertyFileName = propertyFileName; } @@ -95,10 +98,12 @@ public class PropertiesAWSCredentials extends BasicAWSCredentials implements Ini } /**. - * Sets the name of the file that will hold the AW credentials - * @param propertyFileName + * Sets the name of the file that will hold the AW credentials. + * + * @param propertyFileName Must not be null or empty */ public void setPropertyFileName(String propertyFileName) { + Assert.hasText(propertyFileName, "The propertyFileName parameter must not be null or empty."); this.propertyFileName = propertyFileName; } diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/AmazonSESMessageHandler.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/AmazonSESMessageHandler.java index 0403c7d..9465d31 100644 --- a/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/AmazonSESMessageHandler.java +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/AmazonSESMessageHandler.java @@ -26,7 +26,7 @@ import org.springframework.mail.javamail.JavaMailSender; * * @author Amol Nayak * - * @since 1.0 + * @since 0.5 * */ public class AmazonSESMessageHandler extends MailSendingMessageHandler { diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/config/xml/AmazonSESOutboundAdapterParser.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/config/xml/AmazonSESOutboundAdapterParser.java index 8ea1cae..fdb3a68 100644 --- a/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/config/xml/AmazonSESOutboundAdapterParser.java +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/config/xml/AmazonSESOutboundAdapterParser.java @@ -25,7 +25,7 @@ import org.springframework.integration.core.MessageHandler; * * @author Amol Nayak * - * @since 1.0 + * @since 0.5 * */ public class AmazonSESOutboundAdapterParser extends diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/core/AmazonSESMailSendException.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/core/AmazonSESMailSendException.java index a28018a..03bb6da 100644 --- a/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/core/AmazonSESMailSendException.java +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/core/AmazonSESMailSendException.java @@ -24,7 +24,7 @@ import org.springframework.integration.aws.core.AWSOperationException; * * @author Amol Nayak * - * @since 1.0 + * @since 0.5 * */ public class AmazonSESMailSendException extends AWSOperationException { diff --git a/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/core/DefaultAmazonSESMailSender.java b/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/core/DefaultAmazonSESMailSender.java index 9fdc60e..b44b716 100644 --- a/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/core/DefaultAmazonSESMailSender.java +++ b/spring-integration-aws/src/main/java/org/springframework/integration/aws/ses/core/DefaultAmazonSESMailSender.java @@ -15,94 +15,107 @@ */ package org.springframework.integration.aws.ses.core; -import java.io.ByteArrayOutputStream; -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Map; +import java.io.InputStream; +import java.util.Properties; +import javax.mail.NoSuchProviderException; +import javax.mail.Session; +import javax.mail.Transport; import javax.mail.internet.MimeMessage; import org.springframework.integration.aws.core.AWSCredentials; import org.springframework.integration.aws.core.AWSOperationException; import org.springframework.mail.MailException; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.springframework.mail.javamail.MimeMessagePreparator; -import com.amazonaws.auth.BasicAWSCredentials; -import com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClient; -import com.amazonaws.services.simpleemail.model.RawMessage; -import com.amazonaws.services.simpleemail.model.SendRawEmailRequest; +import com.amazonaws.services.simpleemail.AWSJavaMailTransport; /** * The implementation class for sending mail using the Amazon SES service * * @author Amol Nayak + * @author Gunnar Hillert * - * @since 1.0 + * @since 0.5 * */ -public class DefaultAmazonSESMailSender extends JavaMailSenderImpl { - - /**. - * The API class used for sending email using Amazon SES - */ - private final AmazonSimpleEmailServiceClient emailService; - - private final AWSCredentials credentials; +public class DefaultAmazonSESMailSender implements JavaMailSender { public DefaultAmazonSESMailSender(AWSCredentials credentials) { - if(credentials == null) + + if(credentials == null) { throw new AWSOperationException(null, "Credentials cannot be null, provide a non null valid set of credentials"); - this.credentials = credentials; + } + + /* + * Setup JavaMail to use the Amazon Simple Email Service by specifying + * the "aws" protocol. + */ + Properties properties = new Properties(); + properties.setProperty("mail.transport.protocol", "aws"); + + properties.setProperty(AWSJavaMailTransport.AWS_ACCESS_KEY_PROPERTY, credentials.getAccessKey()); + properties.setProperty(AWSJavaMailTransport.AWS_SECRET_KEY_PROPERTY, credentials.getSecretKey()); + + javaMailSender.setJavaMailProperties(properties); - emailService = new AmazonSimpleEmailServiceClient( - new BasicAWSCredentials(credentials.getAccessKey(), - credentials.getSecretKey())); } + private JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl() { + + @Override + protected Transport getTransport(Session session) + throws NoSuchProviderException { + return new AWSJavaMailTransport(session, null); + } + + }; - /** - * The Actual implementation of the sending of the mail message using the AWS SES - * SDK - */ @Override - protected void doSend(MimeMessage[] mimeMessages, Object[] originalMessages) + public void send(SimpleMailMessage simpleMessage) throws MailException { + javaMailSender.send(simpleMessage); + } + + @Override + public void send(SimpleMailMessage[] simpleMessages) throws MailException { + javaMailSender.send(simpleMessages); + } + + @Override + public MimeMessage createMimeMessage() { + return javaMailSender.createMimeMessage(); + } + + @Override + public MimeMessage createMimeMessage(InputStream contentStream) throws MailException { - Map failedMessageMap = new HashMap(); - if(mimeMessages != null && mimeMessages.length > 0) { - int index = 0; - for(MimeMessage mailMessage:mimeMessages) { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - mailMessage.writeTo(baos); - RawMessage rawMessage = new RawMessage(ByteBuffer.wrap(baos.toByteArray())); - SendRawEmailRequest rawMail = new SendRawEmailRequest(rawMessage); - emailService.sendRawEmail(rawMail); - index++; - } catch (Exception e) { - failedMessageMap.put(originalMessages != null? originalMessages[index]:mailMessage, e); - } - } - } - - if(!failedMessageMap.isEmpty()) { - throw new AmazonSESMailSendException(credentials.getAccessKey(), - "Exception while sending one or more messages, see failedMessages for more details", - failedMessageMap); - } + return javaMailSender.createMimeMessage(contentStream); } @Override - public void setPort(int port) { - throw new UnsupportedOperationException("AWS SES Implementation of mail " + - "sender does not allow setting the port number"); + public void send(MimeMessage mimeMessage) throws MailException { + javaMailSender.send(mimeMessage); } - @Override - public void setHost(String host) { - throw new UnsupportedOperationException("AWS SES Implementation of mail " + - "sender does not allow setting the host name. It is already configured with an endpoint"); + public void send(MimeMessage[] mimeMessages) throws MailException { + javaMailSender.send(mimeMessages); + } + + @Override + public void send(MimeMessagePreparator mimeMessagePreparator) + throws MailException { + javaMailSender.send(mimeMessagePreparator); + + } + + @Override + public void send(MimeMessagePreparator[] mimeMessagePreparators) + throws MailException { + javaMailSender.send(mimeMessagePreparators); } - //Should we disallow setSession? } \ No newline at end of file diff --git a/spring-integration-aws/src/test/java/org/springframework/integration/aws/common/AWSClientFactoryTest.java b/spring-integration-aws/src/test/java/org/springframework/integration/aws/common/AWSClientFactoryTest.java index a3a44d5..8bbc2a5 100644 --- a/spring-integration-aws/src/test/java/org/springframework/integration/aws/common/AWSClientFactoryTest.java +++ b/spring-integration-aws/src/test/java/org/springframework/integration/aws/common/AWSClientFactoryTest.java @@ -28,7 +28,7 @@ import com.amazonaws.services.sqs.AmazonSQSClient; * * @author Amol Nayak * - * @since 1.0 + * @since 0.5 * */ public class AWSClientFactoryTest { diff --git a/spring-integration-aws/src/test/java/org/springframework/integration/aws/common/BaseTestCase.java b/spring-integration-aws/src/test/java/org/springframework/integration/aws/common/BaseTestCase.java index 91e501b..74dc96e 100644 --- a/spring-integration-aws/src/test/java/org/springframework/integration/aws/common/BaseTestCase.java +++ b/spring-integration-aws/src/test/java/org/springframework/integration/aws/common/BaseTestCase.java @@ -24,7 +24,7 @@ import java.util.Properties; * * @author Amol Nayak * - * @since 1.0 + * @since 0.5 * */ public abstract class BaseTestCase { diff --git a/spring-integration-aws/src/test/java/org/springframework/integration/aws/config/xml/AbstractAWSOutboundChannelAdapterParserTests.java b/spring-integration-aws/src/test/java/org/springframework/integration/aws/config/xml/AbstractAWSOutboundChannelAdapterParserTests.java index 62f2501..45fb3d7 100644 --- a/spring-integration-aws/src/test/java/org/springframework/integration/aws/config/xml/AbstractAWSOutboundChannelAdapterParserTests.java +++ b/spring-integration-aws/src/test/java/org/springframework/integration/aws/config/xml/AbstractAWSOutboundChannelAdapterParserTests.java @@ -32,7 +32,7 @@ import org.springframework.integration.test.util.TestUtils; * * @author Amol Nayak * - * @since 1.0 + * @since 0.5 * */ public abstract class AbstractAWSOutboundChannelAdapterParserTests { diff --git a/spring-integration-aws/src/test/java/org/springframework/integration/aws/core/AbstractAWSClientFactoryTests.java b/spring-integration-aws/src/test/java/org/springframework/integration/aws/core/AbstractAWSClientFactoryTests.java index 1db3983..f3dece2 100644 --- a/spring-integration-aws/src/test/java/org/springframework/integration/aws/core/AbstractAWSClientFactoryTests.java +++ b/spring-integration-aws/src/test/java/org/springframework/integration/aws/core/AbstractAWSClientFactoryTests.java @@ -30,7 +30,7 @@ import com.amazonaws.AmazonWebServiceClient; * The abstract test class for Amazon WebService client factory tests * @author Amol Nayak * - * @since 1.0 + * @since 0.5 * */ public abstract class AbstractAWSClientFactoryTests { diff --git a/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/AmazonSESMessageHandlerTests.java b/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/AmazonSESMessageHandlerTests.java index 8b3038b..f86a175 100644 --- a/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/AmazonSESMessageHandlerTests.java +++ b/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/AmazonSESMessageHandlerTests.java @@ -48,7 +48,7 @@ import org.springframework.mail.javamail.JavaMailSender; * * @author Amol Nayak * - * @since 1.0 + * @since 0.5 * */ public class AmazonSESMessageHandlerTests { diff --git a/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/config/xml/AmazonSESOutboundAdapterParserTests.java b/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/config/xml/AmazonSESOutboundAdapterParserTests.java index ce60ac1..9ea5e16 100644 --- a/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/config/xml/AmazonSESOutboundAdapterParserTests.java +++ b/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/config/xml/AmazonSESOutboundAdapterParserTests.java @@ -15,22 +15,25 @@ */ package org.springframework.integration.aws.ses.config.xml; +import java.util.Properties; + import junit.framework.Assert; import org.junit.Test; import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; import org.springframework.context.support.ClassPathXmlApplicationContext; -import org.springframework.integration.aws.core.AWSCredentials; import org.springframework.integration.core.MessageHandler; import org.springframework.integration.endpoint.EventDrivenConsumer; import org.springframework.integration.test.util.TestUtils; +import com.amazonaws.services.simpleemail.AWSJavaMailTransport; + /** * The test class for the AmazonSESOutboundAdapterParser * * @author Amol Nayak * - * @since 1.0 + * @since 0.5 * */ public class AmazonSESOutboundAdapterParserTests { @@ -79,10 +82,11 @@ public class AmazonSESOutboundAdapterParserTests { Assert.assertNotNull("Expected a non null EventDrivenConsumer", consumer); MessageHandler handler = TestUtils.getPropertyValue(consumer, "handler", MessageHandler.class); Assert.assertNotNull("Expected a non null messagehandler", handler); - AWSCredentials credentials = TestUtils.getPropertyValue(handler, "mailSender.credentials", AWSCredentials.class); - Assert.assertNotNull("Expected a non null instance of credentials", credentials); - Assert.assertEquals("dummy", credentials.getAccessKey()); - Assert.assertEquals("dummy", credentials.getSecretKey()); + + Properties properties = TestUtils.getPropertyValue(handler, "mailSender.javaMailSender.javaMailProperties", Properties.class); + Assert.assertNotNull("Expected a non null instance of credentials", properties); + Assert.assertEquals("dummy", properties.getProperty(AWSJavaMailTransport.AWS_ACCESS_KEY_PROPERTY)); + Assert.assertEquals("dummy", properties.getProperty(AWSJavaMailTransport.AWS_SECRET_KEY_PROPERTY)); } /** diff --git a/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/core/DefaultAmazonSESMailSenderAWSTests.java b/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/core/DefaultAmazonSESMailSenderAWSTests.java index 95c703b..386e219 100644 --- a/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/core/DefaultAmazonSESMailSenderAWSTests.java +++ b/spring-integration-aws/src/test/java/org/springframework/integration/aws/ses/core/DefaultAmazonSESMailSenderAWSTests.java @@ -15,7 +15,6 @@ */ package org.springframework.integration.aws.ses.core; -import javax.mail.Session; import javax.mail.internet.MimeMessage; import org.junit.BeforeClass; @@ -38,7 +37,7 @@ import org.springframework.mail.javamail.MimeMessageHelper; * * @author Amol Nayak * -* @since 1.0 +* @since 0.5 * */ public class DefaultAmazonSESMailSenderAWSTests { @@ -96,8 +95,7 @@ public class DefaultAmazonSESMailSenderAWSTests { */ @Test public void sendMimeMessage() throws Exception { - Session session = sender.getSession(); - MimeMessageHelper helper = new MimeMessageHelper(new MimeMessage(session)); + MimeMessageHelper helper = new MimeMessageHelper(sender.createMimeMessage()); helper.setText("Some HTML Text", true); helper.setTo(TO_EMAIL_ID); helper.setFrom(TO_EMAIL_ID); @@ -111,16 +109,15 @@ public class DefaultAmazonSESMailSenderAWSTests { */ @Test public void sendMimeMessageArray() throws Exception { - Session session = sender.getSession(); MimeMessage[] messages = new MimeMessage[2]; - MimeMessageHelper helper = new MimeMessageHelper(new MimeMessage(session)); + MimeMessageHelper helper = new MimeMessageHelper(sender.createMimeMessage()); helper.setText("Some HTML Text One", true); helper.setTo(TO_EMAIL_ID); helper.setFrom(TO_EMAIL_ID); helper.setSubject("Some HTML Message's Subject Line One"); MimeMessage message = helper.getMimeMessage(); messages[0] = message; - helper = new MimeMessageHelper(new MimeMessage(session)); + helper = new MimeMessageHelper(sender.createMimeMessage()); helper.setText("Some HTML Text Two", true); helper.setTo(TO_EMAIL_ID); helper.setFrom(TO_EMAIL_ID);