Move AWS to separate repo

This commit is contained in:
Artem Bilan
2014-11-17 13:45:16 +02:00
parent 3268e0a2f7
commit 05b7f39f39
98 changed files with 1 additions and 11312 deletions

View File

@@ -1,140 +1,4 @@
Spring Integration Extension for Amazon Web Services (AWS)
==========================================================
# Introduction
## 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][].
## Spring Integration's extensions to AWS
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)**
* **Amazon Simple Queue Service (SQS)** (Development complete, coming soon)
* **Amazon DynamoDB** (Analysis ongoing)
* **Amazon SimpleDB** (Not initiated)
* **Amazon SNS** (Not initiated)
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.
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
##Amazon Simple Storage Service (Amazon S3)
###Introduction
###Outbound Channel Adapter
###Inbound Channel Adapter
##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
<integration:channel id="outboundAdapterChannel"/>
<int-aws-ses:outbound-channel-adapter
propertiesFile="classpath:awscredentials.properties"
channel="outboundAdapterChannel"/>
*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.
<integration:channel id="outboundAdapterChannel"/>
<int-aws-ses:outbound-channel-adapter
accessKey="{your access key}"
secreKey="{your secret key}"
channel="outboundAdapterChannel"/>
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<String, Object> headers = new HashMap<String, Object>();
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<String> msg =
MessageBuilder.withPayload("<html><i>A Simple Mail Message sent from " +
"Amazon SES Outbound adapter from Spring integration<i></html>")
.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("<File path to the attachment>"));
helper.setSubject("Name Pic");
Message<MimeMessage> 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.
[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/
[http://aws.amazon.com/free/]: http://aws.amazon.com/free/
The project is hosted on https://github.com/spring-projects/spring-integration-aws

View File

@@ -1,267 +0,0 @@
description = 'Spring Integration AWS Support'
buildscript {
repositories {
maven { url 'http://repo.spring.io/plugins-snapshot' }
}
}
apply plugin: 'java'
apply from: "${rootProject.projectDir}/publish-maven.gradle"
apply plugin: 'eclipse'
apply plugin: 'idea'
group = 'org.springframework.integration'
repositories {
maven { url 'http://repo.spring.io/libs-milestone' }
maven { url 'http://repo.spring.io/plugins-release' }
}
sourceCompatibility=1.6
targetCompatibility=1.6
// See http://www.gradle.org/docs/current/userguide/dependency_management.html#sub:configurations
// and http://www.gradle.org/docs/current/dsl/org.gradle.api.artifacts.ConfigurationContainer.html
configurations {
jacoco //Configuration Group used by Sonar to provide Code Coverage using JaCoCo
}
ext {
apacheHttpclientVersion='4.3.4'
apacheHttpCoreVersion='4.3.2'
awsSdkVersion='1.8.7'
commonsCodecVersion='1.9'
commonsIoVersion='2.0.1'
javaMailVersion='1.5.2'
jacksonVersion='1.9.11'
springIntegrationVersion = '4.0.3.RELEASE'
idPrefix = 'aws'
linkHomepage = 'https://github.com/spring-projects/spring-integration-extensions'
linkCi = 'https://build.spring.io/browse/INTEXT'
linkIssue = 'https://jira.spring.io/browse/INTEXT'
linkScmUrl = 'https://github.com/spring-projects/spring-integration-extensions'
linkScmConnection = 'https://github.com/spring-projects/spring-integration-extensions.git'
linkScmDevConnection = 'git@github.com:spring-projects/spring-integration-extensions.git'
}
ext.javadocLinks = [
"http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/"
] as String[]
dependencies {
compile "org.springframework.integration:spring-integration-core:$springIntegrationVersion"
compile "com.amazonaws:aws-java-sdk:$awsSdkVersion"
compile "commons-codec:commons-codec:$commonsCodecVersion"
compile "commons-io:commons-io:$commonsIoVersion"
compile "org.codehaus.jackson:jackson-core-asl:$jacksonVersion"
compile "org.codehaus.jackson:jackson-mapper-asl:$jacksonVersion"
compile "org.apache.httpcomponents:httpclient:$apacheHttpclientVersion"
compile "org.apache.httpcomponents:httpcore:$apacheHttpCoreVersion"
//Amazon SES Support
compile ("org.springframework.integration:spring-integration-mail:$springIntegrationVersion", optional)
compile ("javax.mail:javax.mail-api:$javaMailVersion", optional)
testCompile "org.springframework.integration:spring-integration-test:$springIntegrationVersion"
jacoco group: "org.jacoco", name: "org.jacoco.agent", version: "0.7.1.201405082137", classifier: "runtime"
}
eclipse {
project {
natures += 'org.springframework.ide.eclipse.core.springnature'
}
}
javadoc {
group = 'Documentation'
description = 'Generates the Javadoc API documentation.'
title = "${rootProject.description} ${version} API"
options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED
options.author = true
options.header = project.description
options.overview = 'src/api/overview.html'
options.stylesheetFile = file("src/api/stylesheet.css")
options.splitIndex = true
options.links(project.ext.javadocLinks)
source = sourceSets.main.allJava
classpath = project.sourceSets.main.compileClasspath
destinationDir = new File(buildDir, "api")
// suppress warnings due to cross-module @see and @link references;
// note that global 'api' task does display all warnings.
logging.captureStandardError LogLevel.INFO
logging.captureStandardOutput LogLevel.INFO // suppress "## warnings" message
}
sourceSets {
test {
resources {
srcDirs = ['src/test/resources', 'src/test/java']
}
}
}
// enable all compiler warnings; individual projects may customize further
ext.xLintArg = '-Xlint:all,-options'
[compileJava, compileTestJava]*.options*.compilerArgs = [xLintArg]
test {
// suppress all console output during testing unless running `gradle -i`
logging.captureStandardOutput(LogLevel.INFO)
jvmArgs "-javaagent:${configurations.jacoco.asPath}=destfile=${buildDir}/jacoco.exec,includes=*"
}
task sourcesJar(type: Jar) {
classifier = 'sources'
from sourceSets.main.allJava
}
task javadocJar(type: Jar) {
classifier = 'javadoc'
from javadoc
}
artifacts {
archives sourcesJar
archives javadocJar
}
apply plugin: 'sonar-runner'
sonarRunner {
sonarProperties {
property "sonar.jacoco.reportPath", "${buildDir.name}/jacoco.exec"
property "sonar.links.homepage", linkHomepage
property "sonar.links.ci", linkCi
property "sonar.links.issue", linkIssue
property "sonar.links.scm", linkScmUrl
property "sonar.links.scm_dev", linkScmDevConnection
property "sonar.java.coveragePlugin", "jacoco"
}
}
task schemaZip(type: Zip) {
group = 'Distribution'
classifier = 'schema'
description = "Builds -${classifier} archive containing all " +
"XSDs for deployment at static.springframework.org/schema."
def Properties schemas = new Properties();
def shortName = idPrefix.replaceFirst("${idPrefix}-", '')
project.sourceSets.main.resources.find {
it.path.endsWith('META-INF/spring.schemas')
}?.withInputStream { schemas.load(it) }
for (def key : schemas.keySet()) {
File xsdFile = project.sourceSets.main.resources.find {
it.path.endsWith(schemas.get(key))
}
assert xsdFile != null
into ("integration/${shortName}") {
from xsdFile.path
}
}
}
task docsZip(type: Zip) {
group = 'Distribution'
classifier = 'docs'
description = "Builds -${classifier} archive containing the JavaDoc api " +
"for deployment at static.springframework.org/spring-integration/docs."
from('.') {
include 'README.md'
}
from (javadoc) {
into 'api'
}
}
task distZip(type: Zip, dependsOn: [docsZip, schemaZip]) {
group = 'Distribution'
classifier = 'dist'
description = "Builds -${classifier} archive, containing all jars and docs, " +
"suitable for community download page."
ext.baseDir = "${project.name}-${project.version}";
from('src/dist') {
include 'license.txt'
include 'notice.txt'
into "${baseDir}"
}
from('.') {
include 'README.md'
into "${baseDir}"
}
from(zipTree(schemaZip.archivePath)) {
into "${baseDir}/schema"
}
into ("${baseDir}/libs") {
from project.jar
from project.sourcesJar
from project.javadocJar
}
}
// Create an optional "with dependencies" distribution.
// Not published by default; only for use when building from source.
task depsZip(type: Zip, dependsOn: distZip) { zipTask ->
group = 'Distribution'
classifier = 'dist-with-deps'
description = "Builds -${classifier} archive, containing everything " +
"in the -${distZip.classifier} archive plus all dependencies."
from zipTree(distZip.archivePath)
gradle.taskGraph.whenReady { taskGraph ->
if (taskGraph.hasTask(":${zipTask.name}")) {
def projectName = rootProject.name
def artifacts = new HashSet()
rootProject.configurations.runtime.resolvedConfiguration.resolvedArtifacts.each { artifact ->
def dependency = artifact.moduleVersion.id
if (!projectName.equals(dependency.name)) {
artifacts << artifact.file
}
}
zipTask.from(artifacts) {
into "${distZip.baseDir}/deps"
}
}
}
}
artifacts {
archives distZip
archives docsZip
archives schemaZip
}
task dist(dependsOn: assemble) {
group = 'Distribution'
description = 'Builds -dist, -docs and -schema distribution archives.'
}
task wrapper(type: Wrapper) {
description = 'Generates gradlew[.bat] scripts'
gradleVersion = '1.12'
distributionUrl = "http://services.gradle.org/distributions/gradle-${gradleVersion}-all.zip"
}

View File

@@ -1 +0,0 @@
version=0.5.0.BUILD-SNAPSHOT

View File

@@ -1,6 +0,0 @@
#Thu Aug 14 15:52:49 EEST 2014
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip

View File

@@ -1,164 +0,0 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

View File

@@ -1,90 +0,0 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -1,66 +0,0 @@
apply plugin: 'maven'
ext.optionalDeps = []
ext.providedDeps = []
ext.optional = { optionalDeps << it }
ext.provided = { providedDeps << it }
install {
repositories.mavenInstaller {
customizePom(pom, project)
}
}
def customizePom(pom, gradleProject) {
pom.whenConfigured { generatedPom ->
// respect 'optional' and 'provided' dependencies
gradleProject.optionalDeps.each { dep ->
generatedPom.dependencies.find { it.artifactId == dep.name }?.optional = true
}
gradleProject.providedDeps.each { dep ->
generatedPom.dependencies.find { it.artifactId == dep.name }?.scope = 'provided'
}
// eliminate test-scoped dependencies (no need in maven central poms)
generatedPom.dependencies.removeAll { dep ->
dep.scope == 'test'
}
// add all items necessary for maven central publication
generatedPom.project {
name = gradleProject.description
description = gradleProject.description
url = 'https://github.com/SpringSource/spring-integration-extensions'
organization {
name = 'SpringSource'
url = 'http://springsource.org'
}
licenses {
license {
name 'The Apache Software License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
distribution 'repo'
}
}
scm {
url = 'https://github.com/SpringSource/spring-integration-extensions'
connection = 'scm:git:git://github.com/SpringSource/spring-integration-extensions'
developerConnection = 'scm:git:git://github.com/SpringSource/spring-integration-extensions'
}
developers {
developer {
id = 'amolnayak311'
name = 'Amol Nayak'
email = 'amolnayak311@gmail.com'
}
developer {
id = 'ghillert'
name = 'Gunnar Hillert'
email = 'ghillert@vmware.com'
}
}
}
}
}

View File

@@ -1,22 +0,0 @@
<html>
<body>
This document is the API specification for the Spring Integration AWS Extension
<hr/>
<div id="overviewBody">
<p>
For further API reference and developer documentation, see the
<a href="http://static.springsource.org/spring-integration/reference" target="_top">Spring
Integration reference documentation</a>.
That documentation contains more detailed, developer-targeted
descriptions, with conceptual overviews, definitions of terms,
workarounds, and working code examples.
</p>
<p>
If you are interested in commercial training, consultancy, and
support for Spring Integration, please visit <a href="http://www.springsource.com" target="_top">
http://www.springsource.com</a>
</p>
</div>
</body>
</html>

View File

@@ -1,541 +0,0 @@
/* Javadoc style sheet */
/*
Overall document style
*/
body {
background-color:#ffffff;
color:#353833;
font-family:Arial, Helvetica, sans-serif;
font-size:76%;
margin:0;
}
a:link, a:visited {
text-decoration:none;
color:#4c6b87;
}
a:hover, a:focus {
text-decoration:none;
color:#bb7a2a;
}
a:active {
text-decoration:none;
color:#4c6b87;
}
a[name] {
color:#353833;
}
a[name]:hover {
text-decoration:none;
color:#353833;
}
pre {
font-size:1.3em;
}
h1 {
font-size:1.8em;
}
h2 {
font-size:1.5em;
}
h3 {
font-size:1.4em;
}
h4 {
font-size:1.3em;
}
h5 {
font-size:1.2em;
}
h6 {
font-size:1.1em;
}
ul {
list-style-type:disc;
}
code, tt {
font-size:1.2em;
}
dt code {
font-size:1.2em;
}
table tr td dt code {
font-size:1.2em;
vertical-align:top;
}
sup {
font-size:.6em;
}
/*
Document title and Copyright styles
*/
.clear {
clear:both;
height:0px;
overflow:hidden;
}
.aboutLanguage {
float:right;
padding:0px 21px;
font-size:.8em;
z-index:200;
margin-top:-7px;
}
.legalCopy {
margin-left:.5em;
}
.bar a, .bar a:link, .bar a:visited, .bar a:active {
color:#FFFFFF;
text-decoration:none;
}
.bar a:hover, .bar a:focus {
color:#bb7a2a;
}
.tab {
background-color:#0066FF;
background-image:url(resources/titlebar.gif);
background-position:left top;
background-repeat:no-repeat;
color:#ffffff;
padding:8px;
width:5em;
font-weight:bold;
}
/*
Navigation bar styles
*/
.bar {
background-image:url(resources/background.gif);
background-repeat:repeat-x;
color:#FFFFFF;
padding:.8em .5em .4em .8em;
height:auto;/*height:1.8em;*/
font-size:1em;
margin:0;
}
.topNav {
background-image:url(resources/background.gif);
background-repeat:repeat-x;
color:#FFFFFF;
float:left;
padding:0;
width:100%;
clear:right;
height:2.8em;
padding-top:10px;
overflow:hidden;
}
.bottomNav {
margin-top:10px;
background-image:url(resources/background.gif);
background-repeat:repeat-x;
color:#FFFFFF;
float:left;
padding:0;
width:100%;
clear:right;
height:2.8em;
padding-top:10px;
overflow:hidden;
}
.subNav {
background-color:#dee3e9;
border-bottom:1px solid #9eadc0;
float:left;
width:100%;
overflow:hidden;
}
.subNav div {
clear:left;
float:left;
padding:0 0 5px 6px;
}
ul.navList, ul.subNavList {
float:left;
margin:0 25px 0 0;
padding:0;
}
ul.navList li{
list-style:none;
float:left;
padding:3px 6px;
}
ul.subNavList li{
list-style:none;
float:left;
font-size:90%;
}
.topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited {
color:#FFFFFF;
text-decoration:none;
}
.topNav a:hover, .bottomNav a:hover {
text-decoration:none;
color:#bb7a2a;
}
.navBarCell1Rev {
background-image:url(resources/tab.gif);
background-color:#a88834;
color:#FFFFFF;
margin: auto 5px;
border:1px solid #c9aa44;
}
/*
Page header and footer styles
*/
.header, .footer {
clear:both;
margin:0 20px;
padding:5px 0 0 0;
}
.indexHeader {
margin:10px;
position:relative;
}
.indexHeader h1 {
font-size:1.3em;
}
.title {
color:#2c4557;
margin:10px 0;
}
.subTitle {
margin:5px 0 0 0;
}
.header ul {
margin:0 0 25px 0;
padding:0;
}
.footer ul {
margin:20px 0 5px 0;
}
.header ul li, .footer ul li {
list-style:none;
font-size:1.2em;
}
/*
Heading styles
*/
div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 {
background-color:#dee3e9;
border-top:1px solid #9eadc0;
border-bottom:1px solid #9eadc0;
margin:0 0 6px -8px;
padding:2px 5px;
}
ul.blockList ul.blockList ul.blockList li.blockList h3 {
background-color:#dee3e9;
border-top:1px solid #9eadc0;
border-bottom:1px solid #9eadc0;
margin:0 0 6px -8px;
padding:2px 5px;
}
ul.blockList ul.blockList li.blockList h3 {
padding:0;
margin:15px 0;
}
ul.blockList li.blockList h2 {
padding:0px 0 20px 0;
}
/*
Page layout container styles
*/
.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer {
clear:both;
padding:10px 20px;
position:relative;
}
.indexContainer {
margin:10px;
position:relative;
font-size:1.0em;
}
.indexContainer h2 {
font-size:1.1em;
padding:0 0 3px 0;
}
.indexContainer ul {
margin:0;
padding:0;
}
.indexContainer ul li {
list-style:none;
}
.contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt {
font-size:1.1em;
font-weight:bold;
margin:10px 0 0 0;
color:#4E4E4E;
}
.contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd {
margin:10px 0 10px 20px;
}
.serializedFormContainer dl.nameValue dt {
margin-left:1px;
font-size:1.1em;
display:inline;
font-weight:bold;
}
.serializedFormContainer dl.nameValue dd {
margin:0 0 0 1px;
font-size:1.1em;
display:inline;
}
/*
List styles
*/
ul.horizontal li {
display:inline;
font-size:0.9em;
}
ul.inheritance {
margin:0;
padding:0;
}
ul.inheritance li {
display:inline;
list-style:none;
}
ul.inheritance li ul.inheritance {
margin-left:15px;
padding-left:15px;
padding-top:1px;
}
ul.blockList, ul.blockListLast {
margin:10px 0 10px 0;
padding:0;
}
ul.blockList li.blockList, ul.blockListLast li.blockList {
list-style:none;
margin-bottom:25px;
}
ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList {
padding:0px 20px 5px 10px;
border:1px solid #9eadc0;
background-color:#f9f9f9;
}
ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList {
padding:0 0 5px 8px;
background-color:#ffffff;
border:1px solid #9eadc0;
border-top:none;
}
ul.blockList ul.blockList ul.blockList ul.blockList li.blockList {
margin-left:0;
padding-left:0;
padding-bottom:15px;
border:none;
border-bottom:1px solid #9eadc0;
}
ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast {
list-style:none;
border-bottom:none;
padding-bottom:0;
}
table tr td dl, table tr td dl dt, table tr td dl dd {
margin-top:0;
margin-bottom:1px;
}
/*
Table styles
*/
.contentContainer table, .classUseContainer table, .constantValuesContainer table {
border-bottom:1px solid #9eadc0;
width:100%;
}
.contentContainer ul li table, .classUseContainer ul li table, .constantValuesContainer ul li table {
width:100%;
}
.contentContainer .description table, .contentContainer .details table {
border-bottom:none;
}
.contentContainer ul li table th.colOne, .contentContainer ul li table th.colFirst, .contentContainer ul li table th.colLast, .classUseContainer ul li table th, .constantValuesContainer ul li table th, .contentContainer ul li table td.colOne, .contentContainer ul li table td.colFirst, .contentContainer ul li table td.colLast, .classUseContainer ul li table td, .constantValuesContainer ul li table td{
vertical-align:top;
padding-right:20px;
}
.contentContainer ul li table th.colLast, .classUseContainer ul li table th.colLast,.constantValuesContainer ul li table th.colLast,
.contentContainer ul li table td.colLast, .classUseContainer ul li table td.colLast,.constantValuesContainer ul li table td.colLast,
.contentContainer ul li table th.colOne, .classUseContainer ul li table th.colOne,
.contentContainer ul li table td.colOne, .classUseContainer ul li table td.colOne {
padding-right:3px;
}
.overviewSummary caption, .packageSummary caption, .contentContainer ul.blockList li.blockList caption, .summary caption, .classUseContainer caption, .constantValuesContainer caption {
position:relative;
text-align:left;
background-repeat:no-repeat;
color:#FFFFFF;
font-weight:bold;
clear:none;
overflow:hidden;
padding:0px;
margin:0px;
}
caption a:link, caption a:hover, caption a:active, caption a:visited {
color:#FFFFFF;
}
.overviewSummary caption span, .packageSummary caption span, .contentContainer ul.blockList li.blockList caption span, .summary caption span, .classUseContainer caption span, .constantValuesContainer caption span {
white-space:nowrap;
padding-top:8px;
padding-left:8px;
display:block;
float:left;
background-image:url(resources/titlebar.gif);
height:18px;
}
.contentContainer ul.blockList li.blockList caption span.activeTableTab span {
white-space:nowrap;
padding-top:8px;
padding-left:8px;
display:block;
float:left;
background-image:url(resources/activetitlebar.gif);
height:18px;
}
.contentContainer ul.blockList li.blockList caption span.tableTab span {
white-space:nowrap;
padding-top:8px;
padding-left:8px;
display:block;
float:left;
background-image:url(resources/titlebar.gif);
height:18px;
}
.contentContainer ul.blockList li.blockList caption span.tableTab, .contentContainer ul.blockList li.blockList caption span.activeTableTab {
padding-top:0px;
padding-left:0px;
background-image:none;
float:none;
display:inline;
}
.overviewSummary .tabEnd, .packageSummary .tabEnd, .contentContainer ul.blockList li.blockList .tabEnd, .summary .tabEnd, .classUseContainer .tabEnd, .constantValuesContainer .tabEnd {
width:10px;
background-image:url(resources/titlebar_end.gif);
background-repeat:no-repeat;
background-position:top right;
position:relative;
float:left;
}
.contentContainer ul.blockList li.blockList .activeTableTab .tabEnd {
width:10px;
margin-right:5px;
background-image:url(resources/activetitlebar_end.gif);
background-repeat:no-repeat;
background-position:top right;
position:relative;
float:left;
}
.contentContainer ul.blockList li.blockList .tableTab .tabEnd {
width:10px;
margin-right:5px;
background-image:url(resources/titlebar_end.gif);
background-repeat:no-repeat;
background-position:top right;
position:relative;
float:left;
}
ul.blockList ul.blockList li.blockList table {
margin:0 0 12px 0px;
width:100%;
}
.tableSubHeadingColor {
background-color: #EEEEFF;
}
.altColor {
background-color:#eeeeef;
}
.rowColor {
background-color:#ffffff;
}
.overviewSummary td, .packageSummary td, .contentContainer ul.blockList li.blockList td, .summary td, .classUseContainer td, .constantValuesContainer td {
text-align:left;
padding:3px 3px 3px 7px;
}
th.colFirst, th.colLast, th.colOne, .constantValuesContainer th {
background:#dee3e9;
border-top:1px solid #9eadc0;
border-bottom:1px solid #9eadc0;
text-align:left;
padding:3px 3px 3px 7px;
}
td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover {
font-weight:bold;
}
td.colFirst, th.colFirst {
border-left:1px solid #9eadc0;
white-space:nowrap;
}
td.colLast, th.colLast {
border-right:1px solid #9eadc0;
}
td.colOne, th.colOne {
border-right:1px solid #9eadc0;
border-left:1px solid #9eadc0;
}
table.overviewSummary {
padding:0px;
margin-left:0px;
}
table.overviewSummary td.colFirst, table.overviewSummary th.colFirst,
table.overviewSummary td.colOne, table.overviewSummary th.colOne {
width:25%;
vertical-align:middle;
}
table.packageSummary td.colFirst, table.overviewSummary th.colFirst {
width:25%;
vertical-align:middle;
}
/*
Content styles
*/
.description pre {
margin-top:0;
}
.deprecatedContent {
margin:0;
padding:10px 0;
}
.docSummary {
padding:0;
}
/*
Formatting effect styles
*/
.sourceLineNo {
color:green;
padding:0 30px 0 0;
}
h1.hidden {
visibility:hidden;
overflow:hidden;
font-size:.9em;
}
.block {
display:block;
margin:3px 0 0 0;
}
.strong {
font-weight:bold;
}
/*
Spring
*/
pre.code {
background-color: #F8F8F8;
border: 1px solid #CCCCCC;
border-radius: 3px 3px 3px 3px;
overflow: auto;
padding: 10px;
margin: 4px 20px 2px 0px;
}
pre.code code, pre.code code * {
font-size: 1em;
}
pre.code code, pre.code code * {
padding: 0 !important;
margin: 0 !important;
}

View File

@@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -1,21 +0,0 @@
========================================================================
== NOTICE file corresponding to section 4 d of the Apache License, ==
== Version 2.0, in this case for the Spring Integration distribution. ==
========================================================================
This product includes software developed by
the Apache Software Foundation (http://www.apache.org).
The end-user documentation included with a redistribution, if any,
must include the following acknowledgement:
"This product includes software developed by the Spring Framework
Project (http://www.springframework.org)."
Alternatively, this acknowledgement may appear in the software itself,
if and wherever such third-party acknowledgements normally appear.
The names "Spring", "Spring Framework", and "Spring Integration" must
not be used to endorse or promote products derived from this software
without prior written permission. For written permission, please contact
enquiries@springsource.com.

View File

@@ -1,41 +0,0 @@
/*
* 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.s3.config.xml.AmazonS3InboundChannelAdapterParser;
import org.springframework.integration.aws.s3.config.xml.AmazonS3OutboundChannelAdapterParser;
import org.springframework.integration.aws.ses.config.xml.AmazonSESOutboundAdapterParser;
import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler;
/**
* The namespace handler for "int-aws" namespace
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class AWSNamespaceHandler extends
AbstractIntegrationNamespaceHandler {
public void init() {
this.registerBeanDefinitionParser("ses-outbound-channel-adapter", new AmazonSESOutboundAdapterParser());
this.registerBeanDefinitionParser("s3-outbound-channel-adapter",new AmazonS3OutboundChannelAdapterParser());
this.registerBeanDefinitionParser("s3-inbound-channel-adapter", new AmazonS3InboundChannelAdapterParser());
}
}

View File

@@ -1,69 +0,0 @@
/*
* Copyright 2002-2014 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.messaging.MessageHandler;
import org.w3c.dom.Element;
/**
* The common adapter parser for all AWS Outbound channel adapters
*
* @author Amol Nayak
* @author Rob Harrop
*
* @since 0.5
*
*/
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<? extends MessageHandler> 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
}
}

View File

@@ -1,98 +0,0 @@
/*
* 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.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.aws.core.AWSCredentials;
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 0.5
*
*/
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 AWSCredentials} 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
*/
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;
}
}

View File

@@ -1,39 +0,0 @@
/*
* 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 0.5
*
*/
public interface AWSClientFactory<T extends AmazonWebServiceClient> {
/**
* 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);
}

View File

@@ -1,107 +0,0 @@
/*
* 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.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* The common utility methods for the
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class AWSCommonUtils {
private static final Log logger = LogFactory.getLog(AWSCommonUtils.class);
/**
* Generates the MD5 hash of the file provided
* @param file
*/
public static byte[] getContentsMD5AsBytes(File file) {
DigestInputStream din = null;
final byte[] digestToReturn;
try {
BufferedInputStream bin = new BufferedInputStream(new FileInputStream(file),32768);
din = new DigestInputStream(bin, MessageDigest.getInstance("MD5"));
//Just to update the digest
byte[] dummy = new byte[4096];
for (int i = 1; i > 0; i = din.read(dummy));
digestToReturn = din.getMessageDigest().digest();
}
catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("Caught Exception while generating a MessageDigest instance", e);
}
catch (FileNotFoundException e) {
throw new IllegalStateException("File " + file.getName() + " not found", e);
}
catch(IOException e) {
throw new IllegalStateException("Caught exception while reading from file", e);
}
finally {
IOUtils.closeQuietly(din);
}
return digestToReturn;
}
/**
* Compute the MD5 hash of the provided String
* @param contents The String whose MD5 sun is to be computed
*/
public static byte[] getContentsMD5AsBytes(String contents) {
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
return digest.digest(contents.getBytes());
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(String.format("Unable to digest the input String '%s' using MD5.", contents), e);
}
}
/**
* Encodes the given raw bytes into hex
* @param rawBytes
*/
public static String encodeHex(byte[] rawBytes) throws UnsupportedEncodingException {
return new String(Hex.encodeHex(rawBytes));
}
/**
* Decodes the given base 64 raw bytes
*
* @param rawBytes
*/
public static byte[] decodeBase64(byte[] rawBytes) throws UnsupportedEncodingException {
return Base64.decodeBase64(rawBytes);
}
}

View File

@@ -1,38 +0,0 @@
/*
* 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 0.5
*
*/
public interface AWSCredentials {
/**
* Get the Access key to the Amazon WS account
*/
String getAccessKey();
/**
* Get the Secret key to the Amazon WS account
*/
String getSecretKey();
}

View File

@@ -1,64 +0,0 @@
/*
* 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 0.5
*
*/
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
*/
public String getAccessKey() {
return accessKey;
}
}

View File

@@ -1,157 +0,0 @@
/*
* 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.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 <i>getClient</i> method
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public abstract class AbstractAWSClientFactory<T extends AmazonWebServiceClient> implements AWSClientFactory<T> {
/**
* 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<String, T> clientMap = new ConcurrentHashMap<String, T>();
/**
* 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;
private T defaultEndpointInstance;
/**
* Returns the cached implementation of the {@link AmazonWebServiceClient} based on the URL provided.
* the client instance is acquired using the abstract <i>getClientImplementation</i> 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(endpoint == null) {
if(defaultEndpointInstance == null) {
defaultEndpointInstance = getClientImplementation();
}
return defaultEndpointInstance;
}
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<String, T> getClientMap() {
return new HashMap<String, T>(clientMap);
}
/**
* Clears the complete cache
*/
public final void clear() {
clientMap.clear();
}
/**
* Extracts the endpoint from the URL provided
*
* @return Will return null if the provided url is empty
*/
private String getEndpointFromURL(String stringUrl) {
if(!StringUtils.hasText(stringUrl)) {
return 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
*/
protected abstract T getClientImplementation();
}

View File

@@ -1,93 +0,0 @@
/*
* 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 org.springframework.util.Assert;
/**
* The basic implementation class holding the Access key and the secret
* key for the AWS account .
*
* @author Amol Nayak
*
* @since 0.5
*
*/
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 Must not be null or empty
* @param secretKey Must not be null or empty
*/
public BasicAWSCredentials(String accessKey, String secretKey) {
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;
}
/**
* Get the Access key to the Amazon WS account
*/
public String getAccessKey() {
return accessKey;
}
/**
* Set the Access key to the Amazon WS account
*/
public void setAccessKey(String accessKey) {
Assert.hasText(accessKey, "The accessKey parameter must not be null or empty.");
this.accessKey = accessKey;
}
/**
* Get the Secret key to the Amazon WS account
*/
public String getSecretKey() {
return secretKey;
}
/**.
* Set the Secret key to the Amazon WS account
* @param secretKey
*/
public void setSecretKey(String secretKey) {
Assert.hasText(secretKey, "The secretKey parameter must not be null or empty.");
this.secretKey = secretKey;
}
}

View File

@@ -1,48 +0,0 @@
/*
* 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 0.5
*
*/
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);
}
}

View File

@@ -1,149 +0,0 @@
/*
* 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.Assert;
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 0.5
*
*/
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 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;
}
/**.
* Gets the property name which holds the AWS access key
* @return the Access Key Property
*/
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
*/
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
*/
public void setSecretKeyProperty(String secretKeyProperty) {
this.secretKeyProperty = secretKeyProperty;
}
/**.
* Get the name of the property file that will hold the AWS credentials
*/
public String getPropertyFileName() {
return 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;
}
/**.
* 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));
}
}

View File

@@ -1,134 +0,0 @@
/*
* 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.s3;
import org.springframework.util.StringUtils;
/**
* The abstract file name filter that first filters out the file if it is
* not eligible for filtering based on the name.
* For e.g, if a particular folder on S3 is to be synchronized with the
* local file system, then the name of the key is initially accepted
* only if it corresponds to an object under that folder or sub folder on S3.
* All other keys are ignored
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public abstract class AbstractFileNameFilter implements FileNameFilter {
private volatile String folderName;
private volatile boolean acceptSubFolders;
/* (non-Javadoc)
* @see org.springframework.integration.aws.s3.FileNameFilter#accept(java.lang.String)
*/
public boolean accept(String fileName) {
if(!StringUtils.hasText(fileName))
return false;
if(StringUtils.hasText(folderName)) {
if(fileName.startsWith(folderName)) {
//This file is in the folder or in a child folder or the given folder
String relativePath = fileName.substring(folderName.length());
if(relativePath.length() == 0 || (!acceptSubFolders && relativePath.indexOf("/") != -1)) {
return false;
}
}
else {
return false;
}
}
else {
//Its the folder entry within the bucket
if(!acceptSubFolders && fileName.indexOf("/") != -1) {
return false;
}
}
if(fileName.contains("/")) {
return isFileNameAccepted(fileName.substring(fileName.lastIndexOf("/") + 1));
}
else {
return isFileNameAccepted(fileName);
}
}
/**
* Gets the folder whose file are to be accepted, this path is relative to the
* bucket.
*/
public String getFolderName() {
return folderName;
}
/**
* Sets the base folder name under which which the files will be accepted.
*
* @param folderName
*/
public void setFolderName(String folderName) {
if(StringUtils.hasText(folderName)) {
String trimmedFolderName = folderName.trim();
if("/".equals(trimmedFolderName)) {
trimmedFolderName = null;
}
else {
if(!trimmedFolderName.endsWith("/")) {
trimmedFolderName = trimmedFolderName + "/";
}
if(trimmedFolderName.startsWith("/")) {
trimmedFolderName = trimmedFolderName.substring(1);
}
}
this.folderName = trimmedFolderName;
}
else {
this.folderName = null;
}
}
/**
* Checks the flag if the sub folders are to be accepted or not.
*
*/
public boolean isAcceptSubFolders() {
return acceptSubFolders;
}
/**
* Sets if the sub folders of the folder set in {@link #folderName}
* are to be accepted or not.
*
* @param acceptSubFolders
*/
public void setAcceptSubFolders(boolean acceptSubFolders) {
this.acceptSubFolders = acceptSubFolders;
}
public abstract boolean isFileNameAccepted(String fileName);
}

View File

@@ -1,37 +0,0 @@
/*
* 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.s3;
/**
* Simple {@link FileNameFilter} implementation that accepts all the names.
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class AlwaysTrueFileNamefilter extends AbstractFileNameFilter {
/* (non-Javadoc)
* @see org.springframework.integration.aws.s3.FileNameFilter#accept(java.lang.String)
*/
@Override
public boolean isFileNameAccepted(String fileName) {
return true;
}
}

View File

@@ -1,285 +0,0 @@
/*
* Copyright 2002-2014 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.s3;
import java.io.File;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.messaging.Message;
import org.springframework.integration.aws.core.AWSCredentials;
import org.springframework.integration.aws.s3.core.AbstractAmazonS3Operations;
import org.springframework.integration.aws.s3.core.AmazonS3Operations;
import org.springframework.integration.aws.s3.core.DefaultAmazonS3Operations;
import org.springframework.integration.context.IntegrationObjectSupport;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* The message source used to receive the File instances stored on the local file system
* synchronized from the S3
*
* @author Amol Nayak
* @author Rob Harrop
*
* @since 0.5
*
*/
public class AmazonS3InboundSynchronizationMessageSource extends
IntegrationObjectSupport implements MessageSource<File>,FileEventHandler {
private volatile InboundFileSynchronizer synchronizer;
private volatile String bucket;
private volatile String remoteDirectory;
private volatile File directory;
private volatile AWSCredentials credentials;
private volatile String temporarySuffix = ".writing";
private volatile int maxObjectsPerBatch;
private volatile String fileNameWildcard;
private volatile String fileNameRegex;
private volatile BlockingQueue<File> filesQueue;
private volatile AmazonS3Operations s3Operations;
private volatile boolean acceptSubFolders;
private volatile String awsEndpoint;
//We will hard code the queue capacity here
private final int QUEUE_CAPACITY = 1024;
private volatile StandardEvaluationContext ctx;
private volatile Expression directoryExpression;
public Message<File> receive() {
File headElement = filesQueue.poll();
if(headElement == null) {
synchronizer.synchronizeToLocalDirectory(directory, bucket, remoteDirectory);
//Now check the queue again
headElement = filesQueue.poll();
}
if(headElement != null) {
return MessageBuilder.withPayload(headElement).build();
}
else {
return null;
}
}
@Override
protected void onInit() throws Exception {
Assert.notNull(directoryExpression, "Local directory to synchronize to is not set");
ctx = new StandardEvaluationContext();
BeanFactory factory = getBeanFactory();
if(factory != null) {
ctx.setBeanResolver(new BeanFactoryResolver(factory));
}
String directoryPath = directoryExpression.getValue(ctx,String.class);
directory = new File(directoryPath);
Assert.notNull(directory, "Please provide a valid local directory to synchronize the remote files");
// TODO: Uncomment this once we start supporting auto-create-local-directory
// Assert.isTrue(directory.exists(),
// String.format("Provided directory %s does not exist", directoryPath));
Assert.isTrue(directory.isDirectory(),
String.format("Provided path %s is not a directory", directoryPath));
//instantiate the S3Operations instance
if(s3Operations == null) {
s3Operations = new DefaultAmazonS3Operations(credentials);
}
if(AbstractAmazonS3Operations.class.isAssignableFrom(s3Operations.getClass())) {
AbstractAmazonS3Operations abstractOperation = (AbstractAmazonS3Operations)s3Operations;
abstractOperation.setTemporaryFileSuffix(temporarySuffix);
if(StringUtils.hasText(awsEndpoint)) {
abstractOperation.setAwsEndpoint(awsEndpoint);
}
abstractOperation.afterPropertiesSet();
}
//Now the file operations class
InboundLocalFileOperationsImpl fileOperations = new InboundLocalFileOperationsImpl();
fileOperations.setTemporaryFileSuffix(temporarySuffix);
fileOperations.addEventListener(this);
InboundFileSynchronizationImpl synchronizationImpl =
new InboundFileSynchronizationImpl(s3Operations, fileOperations);
synchronizationImpl.setSynchronizingBatchSize(maxObjectsPerBatch);
if(StringUtils.hasText(fileNameWildcard)) {
synchronizationImpl.setFileWildcard(fileNameWildcard);
}
if(StringUtils.hasText(fileNameRegex)) {
synchronizationImpl.setFileNamePattern(fileNameRegex);
}
synchronizationImpl.setAcceptSubFolders(acceptSubFolders);
synchronizationImpl.afterPropertiesSet();
this.synchronizer = synchronizationImpl;
filesQueue = new ArrayBlockingQueue<File>(QUEUE_CAPACITY);
}
//-- For Spring DI
/**
* Sets the AWSCredential instance to be used
*/
public void setCredentials(AWSCredentials credentials) {
Assert.notNull(credentials, "null 'credentials' provided");
this.credentials = credentials;
}
/**
* The temporary suffix that would be used to indicate that the file is being writtem and the operation
* is not yet complete
*
* @param temporarySuffix
*/
public void setTemporarySuffix(String temporarySuffix) {
Assert.hasText(temporarySuffix,"Provide a non null non empty string as temporary suffix");
this.temporarySuffix = temporarySuffix;
}
/**
* The maximum number of objects those will be retrieved in one batch from Amazon S3 bucket
* as part of the listOperation
*
* @param maxObjectsPerBatch
*/
public void setMaxObjectsPerBatch(int maxObjectsPerBatch) {
Assert.isTrue(maxObjectsPerBatch > 0, "Provide a non sero, non negative number for max objects per batch");
this.maxObjectsPerBatch = maxObjectsPerBatch;
}
/**
* Sets the file's wildcard pattern that would be used to match the objects in S3 bucket
* This attribute is mutually exclusive to fileName regex.
*
* @param fileNameWildcard Must not be empty.
*/
public void setFileNameWildcard(String fileNameWildcard) {
Assert.hasText(fileNameWildcard, "Provided file wildcard is null or empty string");
Assert.isTrue(!StringUtils.hasText(fileNameRegex), "File name regex and wildcard are mutually exclusive");
this.fileNameWildcard = fileNameWildcard;
}
/**
* Sets the regex to be used to match the objects in S3 bucket. This attribute is mutually exclusive
* to fileName regex.
*
* @param fileNameRegex
*/
public void setFileNameRegex(String fileNameRegex) {
Assert.hasText(fileNameRegex, "Provided file regex is null or empty string");
Assert.isTrue(!StringUtils.hasText(fileNameWildcard), "File name regex and wildcard are mutually exclusive");
this.fileNameRegex = fileNameRegex;
}
/**
* Sets the bucket with which the data in local directory is synchronized with.
*
* @param bucket
*/
public void setBucket(String bucket) {
Assert.hasText(bucket, "Provided 'bucket' is null or empty string");
this.bucket = bucket;
}
/**
* Sets the remote directory, this is the directory relative to the provided bucket
* in S3.
*
* @param remoteDirectory
*/
public void setRemoteDirectory(String remoteDirectory) {
Assert.hasText(remoteDirectory, "Provided 'remoteDirectory' is null or empty string");
this.remoteDirectory = remoteDirectory;
}
/**
* Sets the expression to find the local directory where the remote files are synchronized with.
*
* @param directoryExpression Must not be null
*/
public void setDirectory(Expression directoryExpression) {
Assert.notNull(directoryExpression, "provided 'directoryExpression' is null");
this.directoryExpression = directoryExpression;
}
/**
* Sets the {@link AmazonS3Operations} instance that would be used for the receiving
* the objects and listing the objects in the bucket.
*
* @param s3Operations
*/
public void setS3Operations(AmazonS3Operations s3Operations) {
Assert.notNull(s3Operations, "null 's3Operations' instance provided");
this.s3Operations = s3Operations;
}
/**
* Set to true if you want the subfolders of the given remote folder to be synchronized to the
* local directory.
*
* @param acceptSubFolders
*/
public void setAcceptSubFolders(boolean acceptSubFolders) {
this.acceptSubFolders = acceptSubFolders;
}
/**
* The AWS region's endpoint whose bucket(and the subfolder if any) will be synchronized
* by this adapter
*
* @param awsEndpoint
*/
public void setAwsEndpoint(String awsEndpoint) {
Assert.hasText(awsEndpoint, "Given AWS Endpoint has to be non null and non empty string");
this.awsEndpoint = awsEndpoint;
}
//----
public void onEvent(FileEvent event) {
//We are interested in Create new file events only
if(FileOperationType.CREATE.equals(event.getFileOperation())) {
try {
filesQueue.put(event.getFile());
//The call hierarchy is
//if, no file found in queue, then
// receive()
// -> InboundFileSynchronizer.synchronizeToLocalDirectory()
// ->InboundLocalFileOperations.writeToFile()
// ->onEvent()
//If the Queue is full and the thread blocks, the lock in synchronizeToLocalDirectory
//stays and hence preventing further concurrent synchronization
} catch (InterruptedException e) {
logger.error("Interrupted while waiting to put the event on the filesQueue", e);
}
}
}
}

View File

@@ -1,220 +0,0 @@
/*
* Copyright 2002-2014 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.s3;
import static org.springframework.integration.aws.s3.AmazonS3MessageHeaders.METADATA;
import static org.springframework.integration.aws.s3.AmazonS3MessageHeaders.OBJECT_ACLS;
import static org.springframework.integration.aws.s3.AmazonS3MessageHeaders.USER_METADATA;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.Collection;
import java.util.Map;
import org.springframework.expression.Expression;
import org.springframework.messaging.Message;
import org.springframework.integration.aws.core.AWSCredentials;
import org.springframework.integration.aws.s3.core.AmazonS3Object;
import org.springframework.integration.aws.s3.core.AmazonS3OperationException;
import org.springframework.integration.aws.s3.core.AmazonS3Operations;
import org.springframework.integration.handler.AbstractMessageHandler;
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
import org.springframework.util.Assert;
/**
* The Message handler for the S3 outbound channel adapter
*
* @author Amol Nayak
* @author Rob Harrop
*
* @since 0.5
*
*/
public class AmazonS3MessageHandler extends AbstractMessageHandler {
private final AWSCredentials credentials;
private final AmazonS3Operations operations;
private volatile String charset = "UTF-8";
private volatile String bucket;
private volatile ExpressionEvaluatingMessageProcessor<String> remoteDirectoryProcessor;
private volatile FileNameGenerationStrategy fileNameGenerator = new DefaultFileNameGenerationStrategy();
@Override
protected void onInit() throws Exception {
super.onInit();
Assert.hasText(bucket,"Bucket not set'");
Assert.notNull(remoteDirectoryProcessor, "Remote Directory processor should be present, set the remore directory expression");
}
/**
* The constructor that initializes {@link AmazonS3MessageHandler} with the provided
* implementation of {@link AmazonS3Operations} and using the provided {@link AWSCredentials}
*
* @param credentials
* @param operations
*/
public AmazonS3MessageHandler(AWSCredentials credentials,AmazonS3Operations operations) {
Assert.notNull(operations,"s3 operations is null");
Assert.notNull(credentials,"AWS Credentials are null");
this.credentials = credentials;
this.operations = operations;
}
/**
* The handler implementation for the Amazon S3 used to put objects in the remote AWS S3 bucket
* the message should contain a valid payload of type {@link File}, {@link InputStream},
* byte[] or {@link String}. Various predetermined headers as defined in {@link AmazonS3MessageHeaders}
* are extracted from the message and an {@link AmazonS3Object} is constructed that is provided to
* the {@link AmazonS3Operations} implementation to be uploaded in S3.
*
* @param message
*/
@Override
@SuppressWarnings("unchecked")
protected void handleMessageInternal(Message<?> message) throws Exception {
Object payload = message.getPayload();
//The payload can be only of type java.io.File, java.io.InputStream, byte[] or String
File file = null;
InputStream in = null;
//potentially unsafe operation if the types are not as those expected
Map<String, String> userMetaData = getHeaderValue(message,USER_METADATA,Map.class);
Map<String, Object> metaData = getHeaderValue(message,METADATA,Map.class);
Map<String, Collection<String>> objectAcls = getHeaderValue(message,OBJECT_ACLS,Map.class);
AmazonS3ObjectBuilder builder = AmazonS3ObjectBuilder
.getInstance()
.withMetaData(metaData)
.withUserMetaData(userMetaData)
.withObjectACL(objectAcls);
String folder = this.remoteDirectoryProcessor.processMessage(message);
String objectName = this.fileNameGenerator.generateFileName(message);
if(payload instanceof File) {
file = (File)payload;
}
else if (payload instanceof InputStream) {
in = (InputStream)payload;
}
else if(payload instanceof byte[]) {
in = new ByteArrayInputStream((byte[])payload);
}
else if(payload instanceof String) {
in = new ByteArrayInputStream(((String)payload).getBytes(charset));
}
else {
throw new AmazonS3OperationException
(credentials.getAccessKey(),
bucket, objectName, "The Message payload is of unexpected type "
+ payload.getClass().getCanonicalName() + ", only supported types are"
+" java.io.File, java.io.InputStream, byte[] and java.lang.String");
}
if(file != null) {
builder.fromFile(file);
}
else {
builder.fromInputStream(in);
}
AmazonS3Object object = builder.build();
if(logger.isDebugEnabled()) {
logger.debug("Uploading Object to bucket " + bucket + ", to folder " + folder + ", with object name " + objectName);
}
operations.putObject(bucket, folder, objectName, object);
}
/**
* The common helper method that would read the message header and checks if it is of a particular type or not
* @param <T>
* @param message
* @param headerName
* @param expectedType
*/
@SuppressWarnings("unchecked")
private <T> T getHeaderValue(Message<?> message, String headerName, Class<T> expectedType) {
T header = null;
Object genericHeader = message.getHeaders().get(headerName);
if(genericHeader == null) {
return null;
}
if(expectedType.isAssignableFrom(genericHeader.getClass())) {
header = (T)genericHeader;
}
else {
logger.warn("Found header " + USER_METADATA + " in the message but was not of required type");
}
return header;
}
/**
* Sets the charset for the String payload received
* @param charset
*/
public void setCharset(String charset) {
Assert.hasText(charset,"'charset' should be non null, non empty string");
this.charset = charset;
}
/**
* Sets the S3 Bucket to which the files are to be uploaded
* @param bucket
*/
public void setBucket(String bucket) {
Assert.hasText(bucket, "'bucket' should be non null, non empty string");
this.bucket = bucket;
}
/**
* Sets the directory evaluating expression for finding the remote directory in S3
* @param expression
*/
public void setRemoteDirectoryExpression(Expression expression) {
Assert.notNull(expression, "Remote directory expression is null");
remoteDirectoryProcessor = new ExpressionEvaluatingMessageProcessor<String>(expression);
}
/**
* Sets the file name generation strategy
* @param fileNameGenerator
*/
public void setFileNameGenerator(FileNameGenerationStrategy fileNameGenerator) {
Assert.notNull(fileNameGenerator,"File name generator is null");
this.fileNameGenerator = fileNameGenerator;
}
}

View File

@@ -1,34 +0,0 @@
/*
* 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.s3;
/**
* Constants defining the headers containing attributes of the S3 object like
* File Name, User's metadata, Object metadata, Object ACL etc
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public interface AmazonS3MessageHeaders {
//TODO: Get rid of the interface for constants
public static final String FILE_NAME = "file_name";
public static final String USER_METADATA = "user_meta_data";
public static final String METADATA = "meta_data";
public static final String OBJECT_ACLS = "object_acls";
}

View File

@@ -1,207 +0,0 @@
/*
* 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.s3;
import java.io.File;
import java.io.InputStream;
import java.util.Collection;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.integration.aws.s3.core.AmazonS3Object;
import org.springframework.integration.aws.s3.core.AmazonS3ObjectACL;
import org.springframework.integration.aws.s3.core.Grantee;
import org.springframework.integration.aws.s3.core.GranteeType;
import org.springframework.integration.aws.s3.core.ObjectGrant;
import org.springframework.integration.aws.s3.core.ObjectPermissions;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* The convenience builder class for building {@link AmazonS3Object}
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class AmazonS3ObjectBuilder {
private final Log logger = LogFactory.getLog(getClass());
private File file;
private InputStream in;
private Map<String, Object> metaData;
private Map<String, String> userMetaData;
private AmazonS3ObjectACL objectACL;
/**
* Gets a new instance of the builder
*/
public static AmazonS3ObjectBuilder getInstance() {
return new AmazonS3ObjectBuilder();
}
/**
* Sets the file which is to be read for uploading into S3
* @param file
*/
public AmazonS3ObjectBuilder fromFile(File file) {
Assert.notNull(file,"null 'file' object provided");
Assert.isNull(in, "Cannot instantiate using both the InputStream and File");
Assert.isTrue(file.exists(), "Provided File location \""+ file.getAbsolutePath()
+ "\" is invalid");
Assert.isTrue(!file.isDirectory(), "Provided File location \""+ file.getAbsolutePath()
+ "\" is a directory path, please provide a valid file location");
this.file = file;
return this;
}
/**
* Convenience method for setting the File object from the String path
* @param fileLocation
*/
public AmazonS3ObjectBuilder fromLocation(String fileLocation) {
Assert.hasText(fileLocation, "'fileLocation should be non null, non empty string");
return fromFile(new File(fileLocation));
}
/**
* Sets an InputStream from which the data to be uploaded to S3 will be read
* @param in The provided {@link InputStream} must not be null
*/
public AmazonS3ObjectBuilder fromInputStream(InputStream in) {
Assert.notNull(in, "The Stream object provided is null");
Assert.isNull(file, "Cannot instantiate using both the InputStream and File");
this.in = in;
return this;
}
/**
* Use the given user meta data for the file to be uploaded
* @param userMetaData
*/
public AmazonS3ObjectBuilder withUserMetaData(Map<String, String> userMetaData) {
this.userMetaData = userMetaData;
return this;
}
/**
* uses the given metadata for the S3 object to be uploaded
* @param metaData
*/
public AmazonS3ObjectBuilder withMetaData(Map<String, Object> metaData) {
this.metaData = metaData;
return this;
}
/**
* Sets the S3 Object ACL
* @param objectACL
*/
public AmazonS3ObjectBuilder withObjectACL(Map<String, Collection<String>> objectACL) {
//The key can be of three types, the email id, the canonical id of the user or the Group identifier
if(objectACL != null && !objectACL.isEmpty()) {
this.objectACL = new AmazonS3ObjectACL();
for(String key:objectACL.keySet()) {
if(isCanonicalId(key)) {
addPermissions(key, GranteeType.CANONICAL_GRANTEE_TYPE, objectACL.get(key));
}
else if(isGroupIdentifier(key)) {
addPermissions(key, GranteeType.GROUP_GRANTEE_TYPE, objectACL.get(key));
}
else {
//assuming thats an email identifier, not using regex to validate the email
//id. We can add email validation if needed and throw an eexception
//if some unexpected value comes up.
addPermissions(key, GranteeType.EMAIL_GRANTEE_TYPE, objectACL.get(key));
}
}
}
return this;
}
/**
* Internal helper method for adding the Grants to the object ACL
* @param grantee
* @param type
* @param permissions
*/
private void addPermissions(String grantee,GranteeType type,Collection<String> permissions) {
for(String permission:permissions) {
ObjectPermissions objectPermission = getObjectPermission(permission);
this.objectACL.addGrant(new ObjectGrant
(new Grantee(grantee,type), objectPermission));
}
}
/**
* Gets the Appropriate {@link ObjectPermissions} based on the String passed
* @param permission
*/
private ObjectPermissions getObjectPermission(String permission) {
//Types of permission are READ, READ_ACP, WRITE_ACP
if(StringUtils.hasText(permission)) {
String permissionString = permission.trim().replaceAll("[ ]+", "_").toUpperCase();
try {
return ObjectPermissions.valueOf(permissionString);
} catch (IllegalArgumentException e) {
logger.error("Requested Enum not found, see underlying exception for more details", e);
throw e;
}
}
else {
throw new IllegalArgumentException("Empty or null string found for object permission");
}
}
/**
* Method checks if the provided String is a canonical id of the AWS account
* @param identifier
*/
private boolean isCanonicalId(String identifier) {
//Note: we do not check if the given id is a valid canonical id with AWS, we just check if it is in the right format
if(StringUtils.hasText(identifier)) {
if(identifier.length() == 64) {
String replacedString = identifier.trim().replaceAll("[a-fA-F0-9]+","");
return replacedString.length() == 0;
}
}
return false;
}
/**
* Checks if the given identifier corresponds to a group id
* @param identifier
*/
private boolean isGroupIdentifier(String identifier){
if(StringUtils.hasText(identifier)) {
String trimmedIdentifier = identifier.trim();
return trimmedIdentifier.equals("http://acs.amazonaws.com/groups/global/AllUsers")
|| trimmedIdentifier.equals("http://acs.amazonaws.com/groups/global/AuthenticatedUsers");
}
return false;
}
/**
* Builds the {@link AmazonS3Object} from the provided meta data, file/input stream and object ACLs
*/
public AmazonS3Object build() {
Assert.isTrue(!(in == null && file == null),"One of File object or InputStream is required");
return new AmazonS3Object(userMetaData, metaData, in, file,objectACL);
}
}

View File

@@ -1,101 +0,0 @@
/*
* Copyright 2002-2014 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.s3;
import java.io.File;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.messaging.Message;
import org.springframework.integration.util.AbstractExpressionEvaluator;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* The Default file name generation strategy. The strategy does the below steps for file name
* generation
* 1. The expression provided for generation of the file name, it is evaluated and
* if a value is obtained, it is used. By default it used the value present in the
* file_name header.
* 2. Else, if the provided payload is of type {@link File}, then the name of the file is used.
* if the file name ends with the temporary file suffix, the suffix is removed and the
* remainder of the file is used as the name.
* 3. If none of the above two are provided, the file name is the &lt;Message Id&gt;.ext
*
* @author Amol Nayak
* @author Rob Harrop
*
* @since 0.5
*
*/
public class DefaultFileNameGenerationStrategy extends AbstractExpressionEvaluator implements
FileNameGenerationStrategy {
private final Log logger = LogFactory.getLog(DefaultFileNameGenerationStrategy.class);
private volatile String temporarySuffix = ".writing";
private volatile String fileNameExpression = "headers['" + AmazonS3MessageHeaders.FILE_NAME + "']" ;
/* (non-Javadoc)
* @see org.springframework.integration.aws.s3.FileNameGenerationStrategy#generateFileName(org.springframework.messaging.Message)
*/
public String generateFileName(Message<?> message) {
String generatedFileName;
try {
String fileName = evaluateExpression(fileNameExpression, message,String.class);
if(StringUtils.hasText(fileName)) {
return fileName;
}
} catch (Exception e) {
//Some exception while evaluating using expression, continue to the file Name
//Ignore
logger.warn("Exception while evaluating the expression '"
+ fileNameExpression + "' on the message", e);
}
Object payload = message.getPayload();
if(payload instanceof File) {
String fileName = ((File)payload).getName();
if(fileName.endsWith(temporarySuffix)) {
//chop off the temp suffix
generatedFileName = fileName.substring(0, fileName.indexOf(temporarySuffix));
}
else {
generatedFileName = fileName;
}
}
else {
//use the default name generated
generatedFileName = message.getHeaders().getId() + ".ext";
}
if(logger.isInfoEnabled()) {
logger.info("Generated file name is " + generatedFileName);
}
return generatedFileName;
}
public void setTemporarySuffix(String temporarySuffix) {
Assert.hasText(temporarySuffix, "Temporary directory suffix should be non null, non empty");
this.temporarySuffix = temporarySuffix;
}
public void setFileNameExpression(String fileNameExpression) {
Assert.hasText(fileNameExpression, "Remote name generation expression should be non null, non empty string");
this.fileNameExpression = fileNameExpression;
}
}

View File

@@ -1,41 +0,0 @@
/*
* 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.s3;
import java.io.File;
/**
* The interface denoting the file event type and the {@link File} on which the event occurred
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public interface FileEvent {
/**
* The Type of the operation on the {@link File} that occurred
* @return The {@link FileOperationType}
*/
FileOperationType getFileOperation();
/**
* The File on which the given occurred
*/
File getFile();
}

View File

@@ -1,34 +0,0 @@
/*
* 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.s3;
/**
* Callback interface that gets notified upon a file event on local file system which are
* performed from {@link InboundLocalFileOperations}
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public interface FileEventHandler {
/**
* The Method that would be invoked with the specified event that occurred on the file
* @param event the {@link FileEvent}
*/
void onEvent(FileEvent event);
}

View File

@@ -1,34 +0,0 @@
/*
* 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.s3;
/**
* The strategy interface used to filter out file names based on some predetermined criteria
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public interface FileNameFilter {
/**
* Determines whether to accept the file with the given name or not
* @param fileName
* @return true if the file with the given name can be accepted, else false
*/
boolean accept(String fileName);
}

View File

@@ -1,35 +0,0 @@
/*
* Copyright 2002-2014 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.s3;
import org.springframework.messaging.Message;
/**
* The Common interface used to generate the file name for the file to be uploaded to S3
*
* @author Amol Nayak
* @author Rob Harrop
*
* @since 0.5
*
*/
public interface FileNameGenerationStrategy {
/**
* Generates the file name from the given message
*/
String generateFileName(Message<?> message);
}

View File

@@ -1,28 +0,0 @@
/*
* 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.s3;
/**
* The enum for various file operations
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public enum FileOperationType {
CREATE,UPDATE,RENAME,DELETE;
}

View File

@@ -1,349 +0,0 @@
/*
* 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.s3;
import static org.springframework.integration.aws.core.AWSCommonUtils.decodeBase64;
import static org.springframework.integration.aws.core.AWSCommonUtils.encodeHex;
import static org.springframework.integration.aws.core.AWSCommonUtils.getContentsMD5AsBytes;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.integration.aws.s3.core.AmazonS3Object;
import org.springframework.integration.aws.s3.core.AmazonS3Operations;
import org.springframework.integration.aws.s3.core.PaginatedObjectsView;
import org.springframework.integration.aws.s3.core.S3ObjectSummary;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* The implementation for {@link InboundFileSynchronizer}, this implementation will use
* the {@link AmazonS3Operations} to list the objects in the remote bucket on invocation of
* the {@link #synchronizeToLocalDirectory(File, String, String)}. The listed objects will then
* be checked against the
*
* @author Amol Nayak
* @author Christos Kapasakalidis
*
* @since 0.5
*
*/
public class InboundFileSynchronizationImpl implements InboundFileSynchronizer, InitializingBean {
private final Log logger = LogFactory.getLog(getClass());
public static final String CONTENT_MD5 = "Content-MD5";
private final AmazonS3Operations client;
private volatile int maxObjectsPerBatch = 100; //default
private final InboundLocalFileOperations fileOperations;
private volatile FileNameFilter filter;
private volatile String fileWildcard;
private volatile String fileNameRegex;
private final Lock lock = new ReentrantLock();
private volatile boolean acceptSubFolders;
/**
*
* @param client
*/
public InboundFileSynchronizationImpl(AmazonS3Operations client,
InboundLocalFileOperations fileOperations) {
Assert.notNull(client, "AmazonS3Client should be non null");
Assert.notNull(fileOperations, "fileOperations should be non null");
this.client = client;
this.fileOperations = fileOperations;
}
public void afterPropertiesSet() throws Exception {
Assert.isTrue(!(StringUtils.hasText(fileWildcard) && StringUtils.hasText(fileNameRegex)),
"Only one of the file name wildcard string or file name regex can be specified");
if (StringUtils.hasText(fileWildcard)) {
filter = new WildcardFileNameFilter(fileWildcard);
}
else if (StringUtils.hasText(fileNameRegex)) {
filter = new RegexFileNameFilter(fileNameRegex);
}
else {
filter = new AlwaysTrueFileNamefilter(); //Match all
}
if (acceptSubFolders) {
((AbstractFileNameFilter) filter).setAcceptSubFolders(true);
fileOperations.setCreateDirectoriesIfRequired(true);
}
}
/* (non-Javadoc)
* @see org.springframework.integration.aws.s3.InboundFileSynchronizer#synchronizeToLocalDirectory(java.io.File, java.lang.String, java.lang.String)
*/
public void synchronizeToLocalDirectory(File localDirectory, String bucketName, String remoteFolder) {
if (!lock.tryLock()) {
if (logger.isInfoEnabled()) {
logger.info("Sync already in progess");
}
//Prevent concurrent synchronization requests
return;
}
if (logger.isInfoEnabled()) {
logger.info("Starting sync with local directory");
}
//Below sync can take long, above lock ensures only one thread is synchronizing
try {
if (remoteFolder != null && "/".equals(remoteFolder)) {
remoteFolder = null;
}
//Set the remote folder for the filter
if (filter instanceof AbstractFileNameFilter) {
((AbstractFileNameFilter) filter).setFolderName(remoteFolder);
}
String nextMarker = null;
do {
PaginatedObjectsView paginatedView = client.listObjects(bucketName, remoteFolder, nextMarker, maxObjectsPerBatch);
if (paginatedView == null)
break; //No files to sync
nextMarker = paginatedView.getNextMarker();
List<S3ObjectSummary> summaries = paginatedView.getObjectSummary();
for (S3ObjectSummary summary : summaries) {
String key = summary.getKey();
if (key.endsWith("/")) {
continue;
}
if (!filter.accept(key))
continue;
//The folder is the root as the key is relative to bucket
AmazonS3Object s3Object = null;
try {
s3Object = client.getObject(bucketName, "/", key);
synchronizeObjectWithFile(localDirectory, summary, s3Object);
}
finally {
if (s3Object != null && s3Object.getInputStream() != null) {
s3Object.getInputStream().close();
}
}
}
} while (nextMarker != null);
}
catch (IOException e) {
logger.error("Caught Exception while trying to close s3object InputStream", e);
}
finally {
lock.unlock();
if (logger.isInfoEnabled()) {
logger.info("Sync completed");
}
}
}
/**
* Synchronizes the Object with the File on the local file system
* @param localDirectory
* @param summary
*/
private void synchronizeObjectWithFile(File localDirectory, S3ObjectSummary summary,
AmazonS3Object s3Object) {
//Get the complete object data
String key = summary.getKey();
if (key.endsWith("/")) {
return;
}
int lastIndex = key.lastIndexOf("/");
String fileName = key.substring(lastIndex + 1);
String filePath = localDirectory.getAbsolutePath();
if (!filePath.endsWith(File.separator)) {
filePath += File.separator;
}
File baseDirectory;
if (lastIndex > 0) {
//there could very well be previous '/' and thus nested sub folders
String prefixKey = key.substring(0, lastIndex);
String[] folders = prefixKey.split("/");
if (folders.length > 0) {
for (String folder : folders) {
filePath = filePath + folder + File.separator;
}
//create the directory structure
baseDirectory = new File(filePath);
baseDirectory.mkdirs();
}
else {
baseDirectory = localDirectory;
}
}
else {
baseDirectory = localDirectory;
}
File file = new File(filePath + fileName);
if (!file.exists()) {
//File doesnt exist, write the contents to it
try {
fileOperations.writeToFile(baseDirectory, fileName, s3Object.getInputStream());
}
catch (IOException e) {
logger.error("Caught Exception while writing to file " + file.getAbsolutePath());
//continue with next file.
}
}
else {
//Synchronize a file that exists
if (!file.isFile()) {
if (logger.isWarnEnabled()) {
logger.warn("The file " + file.getAbsolutePath() + " is not a regular file, probably a directory, ");
}
return;
}
String eTag = summary.getETag();
String md5Hex = null;
if (isEtagMD5Hash(eTag)) {
//Single thread upload
try {
md5Hex = encodeHex(getContentsMD5AsBytes(file));
}
catch (UnsupportedEncodingException e) {
logger.error("Exception encountered while generating the MD5 hash for the file " + file.getAbsolutePath(), e);
}
if (!eTag.equals(md5Hex)) {
//The local file is different than the one on S3, could be latest but we will still
//sync this with the copy on S3
try {
fileOperations.writeToFile(baseDirectory, fileName, s3Object.getInputStream());
}
catch (IOException e) {
logger.error("Caught Exception while writing to file " + file.getAbsolutePath());
}
}
}
else {
//Multi part upload
//Get the MD5 hash from the headers
Map<String, String> userMetaData = s3Object.getUserMetaData();
String b64MD5 = userMetaData.get(CONTENT_MD5);
if (b64MD5 != null) {
//Need to convert to Hex from Base64
try {
md5Hex = encodeHex(getContentsMD5AsBytes(file));
}
catch (UnsupportedEncodingException e) {
logger.error("Exception encountered while generating the MD5 hash for the file " + file.getAbsolutePath(), e);
}
try {
String remoteHexMD5 = new String(
encodeHex(
decodeBase64(b64MD5.getBytes("UTF-8"))));
if (!md5Hex.equals(remoteHexMD5)) {
//Update only if the local file is not same as remote file
try {
fileOperations.writeToFile(baseDirectory, fileName, s3Object.getInputStream());
}
catch (IOException e) {
logger.error("Caught Exception while writing to file " + file.getAbsolutePath());
}
}
}
catch (UnsupportedEncodingException e) {
//Should never get this, suppress
}
}
else {
//Forcefully update the file
try {
fileOperations.writeToFile(baseDirectory, fileName, s3Object.getInputStream());
}
catch (IOException e) {
logger.error("Caught Exception while writing to file " + file.getAbsolutePath());
}
}
}
}
}
/**
* Checks if the given eTag is a MD5 hash as hex, the hash is 128 bit and hence
* has to be 32 characters in length, also it should contain only hex characters
* In case of multi uploads, it is observed that the eTag contains a "-",
* and hence this method will return false.
*
* @param eTag
*/
private boolean isEtagMD5Hash(String eTag) {
if (eTag == null || eTag.length() != 32) {
return false;
}
return eTag.replaceAll("[a-f0-9A-F]", "").length() == 0;
}
/* (non-Javadoc)
* @see org.springframework.integration.aws.s3.InboundFileSynchronizer#setSynchronizingBatchSize(int)
*/
public void setSynchronizingBatchSize(int batchSize) {
if (batchSize > 0)
this.maxObjectsPerBatch = batchSize;
}
/* (non-Javadoc)
* @see org.springframework.integration.aws.s3.InboundFileSynchronizer#setFileNamePattern(java.lang.String)
*/
public void setFileNamePattern(String fileNameRegex) {
this.fileNameRegex = fileNameRegex;
}
/* (non-Javadoc)
* @see org.springframework.integration.aws.s3.InboundFileSynchronizer#setFileWildcard(java.lang.String)
*/
public void setFileWildcard(String fileWildcard) {
this.fileWildcard = fileWildcard;
}
@Override
public void setAcceptSubFolders(boolean acceptSubFolders) {
this.acceptSubFolders = acceptSubFolders;
}
}

View File

@@ -1,76 +0,0 @@
/*
* 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.s3;
import java.io.File;
/**
* The strategy interface for synchronizion the remote file system with local directory
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public interface InboundFileSynchronizer {
/**
* The operation synchronizes the remote S3 file system with the local directory
* It retrieved new files and updates existing ones with the latest content from S3
* Please note that this method will NOT delete any additional files present on the
* local filesystem
* @param localDirectory The local directory that needs to be synchronized with the Remote S3 bucket
* @param bucketName The name of the bucket whose contents are to be synchronized
* @param remoteFolder The folder name in S3 whose contents are to be synchronized
* use / if the contents of the bucket starting from the root
* are to be synchronized. This operation will only synchronize
* the files resent in the given remote folder and will ignore
* all the folders and sub folder in it.
*/
void synchronizeToLocalDirectory(File localDirectory,String bucketName,String remoteFolder);
/**
* Sets the max number of Files to synchronize at a time. This is the max number of
* Object information retrieved at a time from S3 and synchronized with the Local file system
* before the next set of information is retrieved from S3. Please note if the file name
* pattern is set the total number of files from a batch fetched can be anywhere between
* 0 to the max number of files fetched per batch
* @param batchSize
*/
void setSynchronizingBatchSize(int batchSize);
/**
* Sets the file name regex that will be used to match the key value from S3
* to find a match.
* @param fileNameRegex
*/
void setFileNamePattern(String fileNameRegex);
/**
* Sets the simple file name wildcard to match to match the file e.g., it can be
* set to *.txt to accept all .txt files
*/
void setFileWildcard(String wildcardString);
/**
* Set the value to true to enable the objects to be accepted even if they
* are present in the sub folder of the remote folder passed to
* {@link #synchronizeToLocalDirectory(File, String, String)}
*
* @param acceptSubFolders
*/
void setAcceptSubFolders(boolean acceptSubFolders);
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright 2002-2014 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.s3;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* Interface for performing File Operations on local file system.
* It supports registering an {@link FileEventHandler} instances that
* notifies the operations on the file those were performed
*
* @author Amol Nayak
* @author Rob Harrop
*
* @since 0.5
*
*/
public interface InboundLocalFileOperations {
/**
* Registers an individual event handler.
* @param handler
*/
void addEventListener(FileEventHandler handler);
/**
* Registers a {@link List} of {@link FileEventHandler} instances
* @param handlers
*/
void setEventListeners(List<FileEventHandler> handlers);
/**
* The temporary file suffix that will be used when the file is being written to the filesystem
* @param prefix
*/
void setTemporaryFileSuffix(String prefix);
/**
* Sets the flag to true if directories given are to be created if not present
*
* @param createDirectoriesIfRequired
*/
void setCreateDirectoriesIfRequired(boolean createDirectoriesIfRequired);
/**
* The method will write to the file with the specified name in the specified directory
* from the given {@link InputStream}. Upon completion of the writing the appropriate
* {@link FileEventHandler} instance(s) will be notified with the {@link FileOperationType}
* <i>WRITE</i> and {@link File} instance for the created file.
* @param directory
* @param fileName
* @param in
*/
void writeToFile(File directory,String fileName,InputStream in) throws IOException ;
}

View File

@@ -1,237 +0,0 @@
/*
* 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.s3;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
/**
* The Implementation class for the {@link InboundLocalFileOperations}
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class InboundLocalFileOperationsImpl implements
InboundLocalFileOperations {
private final Log logger = LogFactory.getLog(getClass());
private final List<FileEventHandler> handlers = new ArrayList<FileEventHandler>();
private volatile String tempFileSuffix = ".writing";
private volatile boolean createDirectoriesIfRequired;
/* (non-Javadoc)
* @see org.springframework.integration.aws.s3.InboundLocalFileOperations#addEventListener(org.springframework.integration.aws.s3.FileEventHandler)
*/
@Override
public void addEventListener(FileEventHandler handler) {
Assert.notNull(handler, "Handler instance must non null");
handlers.add(handler);
}
/* (non-Javadoc)
* @see org.springframework.integration.aws.s3.InboundLocalFileOperations#setEventListeners(java.util.List)
*/
@Override
public void setEventListeners(List<FileEventHandler> handlers) {
Assert.notNull(handlers, "Handlers must be non null and non empty");
Assert.notEmpty(handlers, "Handlers must be non null and non empty");
this.handlers.clear();
this.handlers.addAll(handlers);
}
/* (non-Javadoc)
* @see org.springframework.integration.aws.s3.InboundLocalFileOperations#setTemporaryFileSuffix(java.lang.String)
*/
@Override
public void setTemporaryFileSuffix(String tempFileSuffix) {
if(!StringUtils.hasText(tempFileSuffix)) {
return;
}
if(!tempFileSuffix.startsWith(".")) {
this.tempFileSuffix = "." + tempFileSuffix;
}
else {
this.tempFileSuffix = tempFileSuffix;
}
}
/**
* Returns true if create directories if required flag is set to true
*/
public boolean isCreateDirectoriesIfRequired() {
return createDirectoriesIfRequired;
}
/**
* Sets the flag to true if directories given are to be created if not present
*
* @param createDirectoriesIfRequired
*/
@Override
public void setCreateDirectoriesIfRequired(boolean createDirectoriesIfRequired) {
this.createDirectoriesIfRequired = createDirectoriesIfRequired;
}
/* (non-Javadoc)
* @see org.springframework.integration.aws.s3.InboundLocalFileOperations#writeToFile(java.io.File, java.lang.String, java.io.InputStream)
*/
public void writeToFile(File directory, String fileName, InputStream in)
throws IOException {
Assert.notNull(directory, "Provide a non null directory");
Assert.hasText(fileName, "Provide a non null non empty file name");
Assert.notNull(in,"Provide a non null instance of InputStream");
Assert.isTrue(!directory.exists() || directory.isDirectory(),"Provided directory is not a directory");
Assert.isTrue(createDirectoriesIfRequired || directory.exists(),"Provided directories does not exist and create directory flag is false");
if(!directory.exists() && createDirectoriesIfRequired) {
if(!directory.mkdirs()) {
throw new IOException(String.format("Unable to create the directory '%s'", directory.getAbsolutePath()));
}
}
if(!(in instanceof ByteArrayInputStream)
&& !(in instanceof BufferedInputStream)) {
in = new BufferedInputStream(in);
}
String tempFileName = fileName + tempFileSuffix;
byte[] bytes = new byte[4096]; //4K
String absoluteDirectoryPath = directory.getAbsolutePath();
String filePath;
if(absoluteDirectoryPath.endsWith(File.separator)) {
filePath = absoluteDirectoryPath + tempFileName;
}
else {
filePath = absoluteDirectoryPath + File.separator + tempFileName;
}
final File fileToWrite = new File(filePath);
if(!fileToWrite.exists()) {
fileToWrite.createNewFile();
}
FileOutputStream fos = new FileOutputStream(fileToWrite);
BufferedOutputStream bos = new BufferedOutputStream(fos);
for(int read = 0;(read = in.read(bytes)) != -1;) {
bos.write(bytes, 0, read);
}
bos.close();
//Now rename the file
final File dest = new File(filePath.substring(0, filePath.indexOf(tempFileSuffix)));
//ifDestination file exists, delete it
final boolean isSuccessful;
if(dest.exists()) {
boolean isDeleteSuccessful = dest.delete();
if(isDeleteSuccessful) {
if(logger.isDebugEnabled()) {
logger.debug("Delete of file " + dest.getName() + " successful");
}
//now rename the temp file to perm destination file
isSuccessful = renameFile(fileToWrite, dest);
}
else {
if(logger.isWarnEnabled()) {
logger.warn("Deletion of file " + dest.getName() + " not successful, falling back to overwriting the contents");
}
FileCopyUtils.copy(fileToWrite, dest);
boolean deleteTemp = fileToWrite.delete();
if(!deleteTemp && logger.isWarnEnabled()) {
logger.warn("Deletion of " + fileToWrite.getName() + " unsuccessful");
}
isSuccessful = true; //as copy has occurred successfully
}
}
else {
isSuccessful = renameFile(fileToWrite, dest);
}
//notify the listeners
if(!handlers.isEmpty()) {
FileEvent event = new FileEvent() {
public FileOperationType getFileOperation() {
return FileOperationType.CREATE;
}
public File getFile() {
if(isSuccessful) {
return dest;
}
else {
return fileToWrite;
}
}
};
for(FileEventHandler handler:handlers) {
try {
handler.onEvent(event);
} catch (Exception e) {
if(logger.isInfoEnabled())
logger.info("Exception occurred while notifying the handler class "
+ handler.getClass().getName(), e);
}
}
}
}
/**
* Private helper method that is used to rename the source to destination file
*
* @param fileToWrite
* @param dest
*/
private boolean renameFile(final File from, final File to) {
final boolean isSuccessful;
final boolean isRenameSuccessful = from.renameTo(to);
if(isRenameSuccessful) {
if(logger.isDebugEnabled()) {
logger.debug("Renaming of file " + from.getName() + " to "
+ to.getName() + " successful");
}
isSuccessful = isRenameSuccessful;
}
else {
if(logger.isWarnEnabled()) {
logger.warn("Renaming of file " + from.getName() + " to "
+ to.getName() + " unsuccessful");
}
isSuccessful = false;
}
return isSuccessful;
}
}

View File

@@ -1,53 +0,0 @@
/*
* 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.s3;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.RegexFileFilter;
import org.springframework.util.Assert;
/**
* Filters out the files by matching the given File name against the given regex
* Uses Apache Commons IO {@link RegexFileFilter} internally
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class RegexFileNameFilter extends AbstractFileNameFilter {
private final IOFileFilter filter;
/**
* Default constructor accepting the regex
* @param regex
*/
public RegexFileNameFilter(String regex) {
Assert.hasText(regex, "Regex should be non null, non empty String");
filter = new RegexFileFilter(regex);
}
/* (non-Javadoc)
* @see org.springframework.integration.aws.s3.FileNameFilter#accept(java.lang.String)
*/
@Override
public boolean isFileNameAccepted(String fileName) {
return filter.accept(null, fileName);
}
}

View File

@@ -1,55 +0,0 @@
/*
* 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.s3;
import org.apache.commons.io.IOCase;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.springframework.util.Assert;
/**
* Performs wildcard filename filtering based on the wildcard String passed.
* Used Apache Commons IO {@link WildcardFileFilter} to perform the filtering
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class WildcardFileNameFilter extends AbstractFileNameFilter {
private final IOFileFilter filter;
/**
* Default construtor accepting the wildcard string
*
* @param wildcardString
*/
public WildcardFileNameFilter(String wildcardString) {
Assert.hasText(wildcardString, "Wildcard string should be non null, non empty String");
filter = new WildcardFileFilter(wildcardString,IOCase.INSENSITIVE);
//Our checks will be case insensitive
}
/* (non-Javadoc)
* @see org.springframework.integration.aws.s3.FileNameFilter#accept(java.lang.String)
*/
@Override
public boolean isFileNameAccepted(String fileName) {
return filter.accept(null, fileName);
}
}

View File

@@ -1,103 +0,0 @@
/*
* 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.s3.config.xml;
import static org.springframework.integration.aws.config.xml.AmazonWSParserUtils.getAmazonWSCredentials;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.expression.Expression;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.aws.s3.AmazonS3InboundSynchronizationMessageSource;
import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* The channel adapter parser for the S3 inbound parser
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class AmazonS3InboundChannelAdapterParser extends
AbstractPollingInboundChannelAdapterParser {
private static final String S3_BUCKET = "bucket";
private static final String TEMPORARY_SUFFIX = "temporary-suffix";
private static final String S3_OPERATIONS = "s3-operations";
private static final String AWS_ENDPOINT = "aws-endpoint";
private static final String REMOTE_DIRECTORY = "remote-directory";
private static final String LOCAL_DIRECTORY = "local-directory";
private static final String LOCAL_DIRECTORY_EXPRESSION = "local-directory-expression";
private static final String AWS_CREDENTIAL = "credentials";
private static final String MAX_OBJECTS_PER_BATCH = "max-objects-per-batch";
private static final String FILE_NAME_WILDCARD = "file-name-wildcard";
private static final String FILE_NAME_REGEX = "file-name-regex";
private static final String ACCEPT_SUB_FOLDERS = "accept-sub-folders";
/* (non-Javadoc)
* @see org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser#parseSource(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected BeanMetadataElement parseSource(Element element,
ParserContext parserContext) {
String awsCredentials = getAmazonWSCredentials(element, parserContext);
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(AmazonS3InboundSynchronizationMessageSource.class);
builder.addPropertyReference(AWS_CREDENTIAL, awsCredentials);
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, S3_OPERATIONS);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, S3_BUCKET);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, TEMPORARY_SUFFIX);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, REMOTE_DIRECTORY);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, AWS_ENDPOINT);
String directory = element.getAttribute(LOCAL_DIRECTORY);
String directoryExpression = element.getAttribute(LOCAL_DIRECTORY_EXPRESSION);
boolean hasDirectory = StringUtils.hasText(directory);
boolean hasDirectoryExpression = StringUtils.hasText(directoryExpression);
if(!hasDirectory && !hasDirectoryExpression) {
String message =
String.format("One of attributes '%s' and '%s' is required", LOCAL_DIRECTORY, LOCAL_DIRECTORY_EXPRESSION);
throw new BeanDefinitionStoreException(message);
}
if(hasDirectory && hasDirectoryExpression) {
String message =
String.format("Attributes '%s' and '%s' are mutually exclusive to each other", LOCAL_DIRECTORY, LOCAL_DIRECTORY_EXPRESSION);
throw new BeanDefinitionStoreException(message);
}
else {
Expression expr;
if(hasDirectory) {
expr = new LiteralExpression(directory);
}
else {
expr = new SpelExpressionParser().parseExpression(directoryExpression);
}
builder.addPropertyValue("directory", expr);
}
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, MAX_OBJECTS_PER_BATCH);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, ACCEPT_SUB_FOLDERS);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, FILE_NAME_WILDCARD);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, FILE_NAME_REGEX);
return builder.getBeanDefinition();
}
}

View File

@@ -1,159 +0,0 @@
/*
* Copyright 2002-2014 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.s3.config.xml;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.integration.aws.config.xml.AbstractAWSOutboundChannelAdapterParser;
import org.springframework.integration.aws.s3.AmazonS3MessageHandler;
import org.springframework.integration.aws.s3.DefaultFileNameGenerationStrategy;
import org.springframework.integration.aws.s3.core.DefaultAmazonS3Operations;
import org.springframework.integration.config.ExpressionFactoryBean;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.messaging.MessageHandler;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* The namespace parser for outbound-channel-parser for the aws-s3 namespace
*
* @author Amol Nayak
* @author Rob Harrop
*
* @since 0.5
*
*/
public class AmazonS3OutboundChannelAdapterParser extends
AbstractAWSOutboundChannelAdapterParser {
private static final String S3_OPERATIONS = "s3-operations";
private static final String AWS_ENDPOINT = "aws-endpoint";
private static final String S3_BUCKET = "bucket";
private static final String CHARSET = "charset";
private static final String MULTIPART_THRESHOLD = "multipart-upload-threshold";
private static final String TEMPORARY_DIRECTORY = "temporary-directory";
private static final String TEMPORARY_SUFFIX = "temporary-suffix";
private static final String THREADPOOL_EXECUTOR = "thread-pool-executor";
private static final String REMOTE_DIRECTORY = "remote-directory";
private static final String REMOTE_DIRECTORY_EXPRESSION = "remote-directory-expression";
private static final String FILE_NAME_GENERATOR = "file-name-generator";
private static final String FILE_NAME_GENERATION_EXPRESSION = "file-name-generation-expression";
/* (non-Javadoc)
* @see org.springframework.integration.aws.core.config.AbstractAWSOutboundChannelAdapterParser#getMessageHandlerImplementation()
*/
@Override
protected Class<? extends MessageHandler> getMessageHandlerImplementation() {
return AmazonS3MessageHandler.class;
}
/**
* This is where we will be instantiating the AmazonS3Operations instance and
* passing it to the MessageHandler
*/
@Override
protected void processBeanDefinition(BeanDefinitionBuilder builder,
String awsCredentialsGeneratedName,Element element, ParserContext context) {
//TODO: When we will have more than one implementations, also provision with an enum
//for the operation
String s3Operations = element.getAttribute(S3_OPERATIONS);
String operationsService;
if(StringUtils.hasText(s3Operations)) {
//custom implementation provided
if(element.hasAttribute(MULTIPART_THRESHOLD)
|| element.hasAttribute(TEMPORARY_DIRECTORY)
|| element.hasAttribute(TEMPORARY_SUFFIX)
|| element.hasAttribute(THREADPOOL_EXECUTOR)) {
throw new BeanDefinitionStoreException("Attributes '" + MULTIPART_THRESHOLD + "', '"
+ TEMPORARY_DIRECTORY + "', '" + TEMPORARY_SUFFIX + "' and '" + THREADPOOL_EXECUTOR
+ " are mutually exclusive to the '" + S3_OPERATIONS + "' attribute");
}
operationsService = s3Operations;
}
else {
BeanDefinitionBuilder s3OpBuilder = BeanDefinitionBuilder.genericBeanDefinition(DefaultAmazonS3Operations.class);
s3OpBuilder.addConstructorArgReference(awsCredentialsGeneratedName);
IntegrationNamespaceUtils.setValueIfAttributeDefined(s3OpBuilder, element, MULTIPART_THRESHOLD);
IntegrationNamespaceUtils.setValueIfAttributeDefined(s3OpBuilder, element, TEMPORARY_DIRECTORY);
IntegrationNamespaceUtils.setValueIfAttributeDefined(s3OpBuilder, element, TEMPORARY_SUFFIX,"temporaryFileSuffix");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(s3OpBuilder, element, THREADPOOL_EXECUTOR);
IntegrationNamespaceUtils.setValueIfAttributeDefined(s3OpBuilder, element, AWS_ENDPOINT);
operationsService = BeanDefinitionReaderUtils.registerWithGeneratedName(s3OpBuilder.getBeanDefinition(), context.getRegistry());
}
//Set the bucket and charset
builder.addConstructorArgReference(operationsService);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, CHARSET);
builder.addPropertyValue(S3_BUCKET, element.getAttribute(S3_BUCKET)); //Mandatory
//Get the remote directory expression or remote directory literal string
String remoteDirectoryLiteral = element.getAttribute(REMOTE_DIRECTORY);
String remoteDirectoryExpression = element.getAttribute(REMOTE_DIRECTORY_EXPRESSION);
boolean hasRemoteDirectoryExpression = StringUtils.hasText(remoteDirectoryExpression);
boolean hasRemoteDirectoryLiteral = StringUtils.hasText(remoteDirectoryLiteral);
if(!(hasRemoteDirectoryExpression ^ hasRemoteDirectoryLiteral)) {
throw new BeanDefinitionStoreException("Exactly one of " + REMOTE_DIRECTORY + " or "
+ REMOTE_DIRECTORY_EXPRESSION + " is required");
}
AbstractBeanDefinition expression;
if(hasRemoteDirectoryLiteral) {
expression = BeanDefinitionBuilder.genericBeanDefinition(LiteralExpression.class)
.addConstructorArgValue(remoteDirectoryLiteral)
.getBeanDefinition();
}
else {
expression = BeanDefinitionBuilder.genericBeanDefinition(ExpressionFactoryBean.class)
.addConstructorArgValue(remoteDirectoryExpression)
.getBeanDefinition();
}
builder.addPropertyValue("remoteDirectoryExpression", expression);
//Get the File generation strategy
String fileNameGenerator = element.getAttribute(FILE_NAME_GENERATOR);
String fileNameGenerationExpression = element.getAttribute(FILE_NAME_GENERATION_EXPRESSION);
boolean hasFileGenerator = StringUtils.hasText(fileNameGenerator);
boolean hasFileGenerationExpression = StringUtils.hasText(fileNameGenerationExpression);
if(hasFileGenerationExpression && hasFileGenerator) {
throw new BeanDefinitionStoreException("Attributes '" + FILE_NAME_GENERATION_EXPRESSION + "' and '"
+ FILE_NAME_GENERATOR + "' are mutually exclusive, at most one might be specified");
}
if(hasFileGenerator) {
builder.addPropertyReference("fileNameGenerator", fileNameGenerator);
}
else {
BeanDefinitionBuilder fileNameGeneratorBuilder =
BeanDefinitionBuilder.genericBeanDefinition(DefaultFileNameGenerationStrategy.class);
String tempDirectorySuffix = element.getAttribute(TEMPORARY_SUFFIX);
if(StringUtils.hasText(tempDirectorySuffix)) {
fileNameGeneratorBuilder.addPropertyValue("temporarySuffix",tempDirectorySuffix);
}
if(hasFileGenerationExpression) {
fileNameGeneratorBuilder.addPropertyValue("fileNameExpression", fileNameGenerationExpression);
}
builder.addPropertyValue("fileNameGenerator", fileNameGeneratorBuilder.getBeanDefinition());
}
}
}

View File

@@ -1,509 +0,0 @@
/*
* 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.s3.core;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.integration.aws.core.AWSCommonUtils;
import org.springframework.integration.aws.core.AWSCredentials;
import org.springframework.util.Assert;
/**
* The common super class for any implementation of {@link AmazonS3Operations}. The sub class
* has to implement all the functionality for performing the actual work to add, remove, update
* delete, list the objects in an S3 bucket
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public abstract class AbstractAmazonS3Operations implements AmazonS3Operations, InitializingBean {
protected final Log logger = LogFactory.getLog(getClass());
private volatile long multipartUploadThreshold;
private volatile File temporaryDirectory = new File(System.getProperty("java.io.tmpdir"));
private volatile String temporaryFileSuffix = ".writing";
public final String PATH_SEPARATOR = "/";
private final AWSCredentials credentials;
private String awsEndpoint;
/**
* The constructor that accepts the {@link AWSCredentials}
* @param credentials
*/
protected AbstractAmazonS3Operations(AWSCredentials credentials) {
Assert.notNull(credentials, "null 'credentials' provided");
this.credentials = credentials;
}
/**
* Get the threshold value in bytes above which multi part upload will be used
*/
public long getMultipartUploadThreshold() {
return multipartUploadThreshold;
}
/**
* The threshold value in bytes above which the service will use multi part upload.
* All the uploads below this value will be uploaded in a single thread
* Minimum value for the threshold is 5120 Bytes (5 KB).
* It is recommended by Amazon to use Multi part uploads for all the uploads
* above 100 MB
* If the value is set to a number above Integer.MAX_VALUE, the value will be
* set to Integer.MAX_VALUE.
*
* @param multipartUploadThreshold
*/
public void setMultipartUploadThreshold(long multipartUploadThreshold) {
Assert.isTrue(multipartUploadThreshold >= 5120,
"Minimum threshold for multipart upload is 5120 bytes");
this.multipartUploadThreshold = multipartUploadThreshold;
}
/**
* Gets the temporary directory
*/
public File getTemporaryDirectory() {
return temporaryDirectory;
}
/**
* The temporary directory that will be used to write the files received over stream
* @param temporaryDirectory
*/
public void setTemporaryDirectory(File temporaryDirectory) {
Assert.notNull(temporaryDirectory, "Provided temporaryDirectory is null");
Assert.isTrue(temporaryDirectory.exists(), "The given temporary directory does not exist");
Assert.isTrue(temporaryDirectory.isDirectory(), "The given temporary directory path has to be a directory");
this.temporaryDirectory = temporaryDirectory;
}
/**
* The temporary directory that will be used to write the files received over stream
* @param temporaryDirectory
*/
public void setTemporaryDirectory(String temporaryDirectory) {
Assert.hasText(temporaryDirectory, "Provided temporary directory string is null or empty string");
setTemporaryDirectory(new File(temporaryDirectory));
}
/**
* Gets the temporary file suffix that is appended to the file while writing to
* the temporary directory
*/
public String getTemporaryFileSuffix() {
return temporaryFileSuffix;
}
/**
* Gets the temporary file suffix
* @param temporaryFileSuffix
*/
public void setTemporaryFileSuffix(String temporaryFileSuffix) {
Assert.hasText(temporaryFileSuffix, "The temporary file suffix must not be null or empty");
if (!temporaryFileSuffix.startsWith(".")) {
temporaryFileSuffix = "." + temporaryFileSuffix;
}
this.temporaryFileSuffix = temporaryFileSuffix;
}
/**
* Gets the AWS endpoint to use for all the operations, by default if none is set then us-east-1 is
* assumed.
*
*/
public String getAwsEndpoint() {
return awsEndpoint;
}
/**
* Sets the valid AWS endpoint to be used by the client to connect to appropriate
* region to perform the operations
*
* @param awsEndpoint
*/
public void setAwsEndpoint(String awsEndpoint) {
Assert.hasText(awsEndpoint, "Given AWS Endpoint has to be non null and non empty string");
this.awsEndpoint = awsEndpoint;
}
/**
* The implemented afterPropertiesSet method
*/
public final void afterPropertiesSet() throws Exception {
//TODO: protect by lock?
init();
}
/**
* The subclass needs to override this method if it desires to perform any initializing
* of the class
*/
protected void init() {
}
/**
* Reads the stream provided and writes the file to the temp location
* @param in the Stream from which the data of the Object is to be read
* @param objectName the name of the object that would be used to upload the file
*/
private File getTempFile(InputStream in, String bucketName, String objectName) {
InputStream inStream;
if (!(in instanceof BufferedInputStream) && !(in instanceof ByteArrayInputStream)) {
inStream = new BufferedInputStream(in);
}
else {
inStream = in;
}
String fileName;
if (objectName.contains(PATH_SEPARATOR)) {
String[] splits = objectName.split(PATH_SEPARATOR);
fileName = splits[splits.length - 1];
}
else {
fileName = objectName;
}
File temporaryDirectory = getTemporaryDirectory();
String temporaryFileSuffix = getTemporaryFileSuffix();
String filePath = temporaryDirectory.getAbsoluteFile() + File.separator + fileName + temporaryFileSuffix;
if (logger.isDebugEnabled()) {
logger.debug("Temporary file path is " + filePath);
}
//Write data to temporary file
File tempFile = new File(filePath);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(tempFile);
byte[] bytes = new byte[1024];
int read = 0;
while (true) {
read = inStream.read(bytes);
if (read == -1) {
break;
}
fos.write(bytes, 0, read);
}
}
catch (FileNotFoundException e) {
throw new AmazonS3OperationException(credentials.getAccessKey(),
bucketName,
objectName,
"Exception caught while writing the temporary file from input stream", e);
}
catch (IOException ioe) {
throw new AmazonS3OperationException(credentials.getAccessKey(),
bucketName,
objectName,
"Exception caught while reading from the provided input stream", ioe);
}
finally {
if (fos != null) {
try {
fos.flush();
fos.close();
}
catch (IOException e) {
//just log
logger.error("Unable to close the stream to the temp file being created", e);
}
try {
in.close();
}
catch (IOException e) {
//just log
logger.error("Unable to close the input stream source", e);
}
}
}
return tempFile;
}
/**
* The implementation of the core common operations that would be performed
* before and after the subclass does the actual work of putting the object to
* the given bucket
*
* @param bucketName The bucket to which the object is to be put
* @param folder The folder to which the object is to be uploaded
* @param objectName The name of the object in the given bucket
* @param s3Object The {@link AmazonS3Object} instance that represents the object
* to be uploaded.
*/
@Override
public final void putObject(String bucketName, String folder, String objectName,
AmazonS3Object s3Object) {
Assert.hasText(bucketName, "null or empty bucketName provided");
Assert.hasText(objectName, "null or empty object name provided");
Assert.notNull(s3Object, "null s3 object provided for upload");
File file = s3Object.getFileSource();
InputStream in = s3Object.getInputStream();
Assert.isTrue(file != null ^ in != null,
"Exactly one of file or inpuut stream needed in the provided s3 object");
boolean isTempFile = false;
if (in != null) {
//We don't know the source of the stream and hence we read the content
//and write to the temporary file.
file = getTempFile(in, bucketName, objectName);
isTempFile = true;
}
String key = getKeyFromFolder(folder, objectName);
//if the size of the file is greater than the threshold for multipart upload,
//set the Content-MD5 header for this upload. This header will also come handy
//later in inbound-channel-adapter where we cant find the MD5 sum of the
//multipart upload file from its ETag
String stringContentMD5 = null;
try {
stringContentMD5 =
AWSCommonUtils.encodeHex(AWSCommonUtils.getContentsMD5AsBytes(file));
}
catch (UnsupportedEncodingException e) {
logger.error("Exception while generating the content's MD5 of the file " + file.getAbsolutePath(), e);
}
try {
doPut(bucketName, key, file, s3Object.getObjectACL(),
s3Object.getUserMetaData(), stringContentMD5);
}
catch (Exception e) {
throw new AmazonS3OperationException(
credentials.getAccessKey(), bucketName,
key,
"Encountered exception while putting an object, see root cause for more details",
e);
}
if (isTempFile) {
//Delete the temp file
if (logger.isDebugEnabled()) {
logger.debug("Deleting temp file: " + file.getName());
}
boolean deleteSuccessful = file.delete();
if (!deleteSuccessful) {
logger.warn("Unable to delete file '" + file.getName() + "'");
}
}
}
/**
* The private method that takes the folder and the object name as the parameters and
* constructs the key that would be for the item to be retrieved or put in the bucket
*
* @param folder
* @param objectName
*/
private String getKeyFromFolder(String folder, String objectName) {
if (objectName.startsWith(PATH_SEPARATOR)) {
//remove the leading / of the object name
objectName = objectName.substring(1);
}
String key;
if (folder != null) {
key = folder.endsWith(PATH_SEPARATOR) ?
folder + objectName : folder + PATH_SEPARATOR + objectName;
}
else {
key = objectName;
}
//check if the foldername begins with a /, if yes, remove it as well as it created
//one directory with blank name
if (key.startsWith(PATH_SEPARATOR)) {
key = key.substring(1);
}
return key;
}
/**
* Deletes the object with the given name from the provided bucket and folder.
*
*/
public boolean removeObject(String bucketName, String folder,
String objectName) {
throw new UnsupportedOperationException("Operation not et supported");
}
/**
* List the objects in a given bucket in the given folder.
*
* @param bucketName The bucket in which we want to list the objects in
* @param nextMarker The number of objects can be very large and this serves as the marker
* for remembering the last record fetch in the last retrieve operation.
* @param pageSize The max number of records to be retrieved in one list object operation.
*
*/
public final PaginatedObjectsView listObjects(String bucketName, String folder, String nextMarker, int pageSize) {
Assert.hasText(bucketName, "Bucket name should be non null and non empty");
Assert.isTrue(pageSize >= 0, "Page size should be a non negative number");
if (logger.isDebugEnabled()) {
logger.debug("Listing objects from bucket " + bucketName + " and folder " + folder);
logger.debug("Next marker is " + nextMarker + " and pageSize is " + pageSize);
}
String prefix = null;
if (folder != null && !PATH_SEPARATOR.equals(folder)) {
prefix = folder;
}
//check if the prefix begins with /
if (prefix != null && prefix.startsWith(PATH_SEPARATOR)) {
prefix = prefix.substring(1);
}
return doListObjects(bucketName, nextMarker, pageSize, prefix);
}
/**
* Gets the object from the given bucket, folder and the name.
*
* @param bucketName The bucket from which to retrieve the object
* @param folder The folder name
* @param objectName The name of the object to retrieve
*/
public final AmazonS3Object getObject(String bucketName, String folder,
String objectName) {
Assert.hasText(bucketName, "Bucket name should be non null and non empty");
if (logger.isDebugEnabled()) {
logger.debug("Getting from bucket " + bucketName +
", from folder " + folder + " the object name " + objectName);
}
String key = getKeyFromFolder(folder, objectName);
try {
return doGetObject(bucketName, key);
}
catch (Exception e) {
throw new AmazonS3OperationException(
credentials.getAccessKey(), bucketName,
key,
"Encountered exception while putting an object, see root cause for more details",
e);
}
}
/**
* Gets the object from the given bucket with the given key using the AWS SDK implementation
*
* @param bucketName
* @param key
*/
protected abstract AmazonS3Object doGetObject(String bucketName, String key);
/**
* The implementation should use the appropriate API to list objects from the given bucket
*
* @param bucketName The bucket in which we want to list the objects in
* @param nextMarker The number of objects can be very large and this serves as the marker
* for remembering the last record fetch in the last retrieve operation.
* @param pageSize The max number of records to be retrieved in one list object operation.
* @param prefix The prefix for the list operation, this can serve as the folder whose contents
* are to be listed.
*/
protected abstract PaginatedObjectsView doListObjects(String bucketName,
String nextMarker, int pageSize, String prefix);
/**
* The abstract method to be implemented by the subclass that would be doing the job
* of uploading the given file against the given key in the given bucket
*
* @param bucket The bucket on S3 where this object is to be put
* @param key The key against which this Object is to be stored in S3
* @param file resource to be uploaded to S3
* @param objectACL the Object's Access controls for the object to be uploaded
* @param userMetadata The user's metadata to be associated with the object uploaded
* @param stringContentMD5 The MD5 sum of the contents of the file to be uploaded
*
*/
protected abstract void doPut(String bucket, String key, File file,
AmazonS3ObjectACL objectACL, Map<String, String> userMetadata, String stringContentMD5);
static class PagninatedObjectsViewImpl implements PaginatedObjectsView {
private final List<S3ObjectSummary> objectSummary;
private final String nextMarker;
public PagninatedObjectsViewImpl(List<S3ObjectSummary> objectSummary,
String nextMarker) {
this.objectSummary = objectSummary;
this.nextMarker = nextMarker;
}
public List<S3ObjectSummary> getObjectSummary() {
return objectSummary != null ? objectSummary : new ArrayList<S3ObjectSummary>();
}
public boolean hasMoreResults() {
return nextMarker != null;
}
public String getNextMarker() {
return nextMarker;
}
}
}

View File

@@ -1,125 +0,0 @@
/*
* 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.s3.core;
import java.io.File;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Collections;
import java.util.Map;
import org.springframework.util.Assert;
/**
* The Amazon S3 Object representing the Object in S3
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class AmazonS3Object implements Serializable {
/**
*
*/
private static final long serialVersionUID = -832622119907619624L;
private Map<String, String> userMetaData;
private Map<String, Object> metaData;
private final InputStream inputStream;
private final File fileSource;
private final AmazonS3ObjectACL objectACL;
/**
* The default constructor
*
* @param userMetaData
* @param metaData
* @param inputStream
*/
public AmazonS3Object(Map<String, String> userMetaData,
Map<String, Object> metaData, InputStream inputStream,File fileSource,AmazonS3ObjectACL objectACL) {
if(userMetaData != null)
this.userMetaData = Collections.unmodifiableMap(userMetaData);
if(metaData != null)
this.metaData = Collections.unmodifiableMap(metaData);
Assert.isTrue((inputStream == null) ^ (fileSource == null),
"Exactly one of 'inputStream' or 'fileSource' must be provided");
this.inputStream = inputStream;
this.fileSource = fileSource;
this.objectACL = objectACL;
}
/**
* The constructor that delegates to {@link #AmazonS3Object(Map, Map, InputStream, File, AmazonS3ObjectACL)}
* with null {@link AmazonS3ObjectACL}
*
* @param userMetaData
* @param metaData
* @param inputStream
* @param fileSource
*/
public AmazonS3Object(Map<String, String> userMetaData,
Map<String, Object> metaData, InputStream inputStream,File fileSource) {
this(userMetaData,metaData,inputStream,fileSource,null);
}
/**
* Gets the User Metadata associated with given Amazon S3 object
*/
public Map<String, String> getUserMetaData() {
return userMetaData;
}
/**
* Gets the Metadata associated with the given Amazon S3 object
*/
public Map<String, Object> getMetaData() {
return metaData;
}
/**
* Gets the {@link InputStream} to the resource
*/
public InputStream getInputStream() {
return inputStream;
}
/**
* Gets the file source
*/
public File getFileSource() {
return fileSource;
}
/**
* Gets the Access controls associated with the S3 Object
*/
public AmazonS3ObjectACL getObjectACL() {
return objectACL;
}
}

View File

@@ -1,62 +0,0 @@
/*
* 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.s3.core;
import java.util.HashSet;
import java.util.Set;
import org.springframework.util.Assert;
/**
* The object containing the Amazon S3 Object's ACL. Access is used to control the access to
* the resource in S3 bucket
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class AmazonS3ObjectACL {
private Set<ObjectGrant> grants = new HashSet<ObjectGrant>();
/**
* Gets all the grants on the object in the bucket
*/
public Set<ObjectGrant> getGrants() {
return grants;
}
/**
* Sets the provided grants on the S3 object
*
* @param grants
*/
public void setGrants(Set<ObjectGrant> grants) {
Assert.notNull(grants, "Provide non null 'grants'");
this.grants = grants;
}
/**
* A convenience method that will be used to add grants to the objects.
*
* @param grant
*/
public void addGrant(ObjectGrant grant) {
Assert.notNull(grant,"Provide non null object grant");
grants.add(grant);
}
}

View File

@@ -1,119 +0,0 @@
/*
* 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.s3.core;
import org.springframework.integration.aws.core.AWSOperationException;
/**
* A subclass of {@link AWSOperationException} which indicates a failure in performing
* an operation on the object in S3.
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class AmazonS3OperationException extends AWSOperationException {
/**
*
*/
private static final long serialVersionUID = 9518185510906801L;
private final String bucket;
private final String objectName;
/**
*The constructor that instantiates with the Account's access key the bucket name
*and the object name
*
* @param accessKey
* @param bucket
* @param objectName
*/
public AmazonS3OperationException(String accessKey, String bucket,
String objectName) {
super(accessKey);
this.bucket = bucket;
this.objectName = objectName;
}
/**
* The constructor that instantiates with the Account's access key the bucket name
* the object name, the exception message and the actual exception
*
* @param accessKey
* @param bucket
* @param objectName
* @param message
* @param cause
*/
public AmazonS3OperationException(String accessKey, String bucket,
String objectName,String message,
Throwable cause) {
super(accessKey, message, cause);
this.bucket = bucket;
this.objectName = objectName;
}
/**
* The constructor that instantiates with the Account's access key the bucket name
* the object name, the exception message
*
* @param accessKey
* @param bucket
* @param objectName
* @param message
*/
public AmazonS3OperationException(String accessKey, String bucket,
String objectName,String message) {
super(accessKey, message);
this.bucket = bucket;
this.objectName = objectName;
}
/**
* The constructor that instantiates with the Account's access key the bucket name
* the object name, the root cause
*
* @param accessKey
* @param bucket
* @param objectName
* @param cause
*/
public AmazonS3OperationException(String accessKey, String bucket,
String objectName,Throwable cause) {
super(accessKey, cause);
this.bucket = bucket;
this.objectName = objectName;
}
/**
* Gets the bucket name for which an S3 operation failed
*/
public String getBucket() {
return bucket;
}
/**
* Gets the object name where an S3 operation failed
*/
public String getObjectName() {
return objectName;
}
}

View File

@@ -1,75 +0,0 @@
/*
* 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.s3.core;
/**
* The Core interface for performing various operations on Amazon S3 like listing objects
* in the bucket, get an object, put an object and remove an object
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public interface AmazonS3Operations {
public static final String CONTENT_MD5_HEADER = "Content-MD5";
/**
* Lists Objects in the given bucket and given folder. Provide / if you
* wish to list objects at the root of the bucket
*
* @param bucketName
* @param folder
* @param nextMarker
* @param pageSize
* @return the {@link PaginatedObjectsView} of the matching result
*/
PaginatedObjectsView listObjects(String bucketName,String folder,String nextMarker,int pageSize);
/**
* Put the given {@link AmazonS3Object} in the provided bucket in the folder specified with the name given
* The object if exists, will be overwritten and the folder path hierarchy
* if absent will be created
*
* @param bucketName
* @param folder
* @param objectName
* @param s3Object
*/
void putObject(String bucketName,String folder,String objectName,AmazonS3Object s3Object);
/**
* Gets the Object from Amazon S3 from the specified bucket,folder and with
* the given objectName
*
* @param bucketName
* @param folder
* @param objectName
* @return The S3 object corresponding to the given details. Null if no object found
*/
AmazonS3Object getObject(String bucketName,String folder,String objectName);
/**
* Removes the specified object from the bucket given, folder specified
* and the given object name from S3
* @param bucketName
* @param folder
* @param objectName
* @return true if the object was successfully removed else false
*/
boolean removeObject(String bucketName,String folder,String objectName);
}

View File

@@ -1,351 +0,0 @@
/*
* 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.s3.core;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
import org.springframework.integration.aws.core.AWSCredentials;
import org.springframework.integration.aws.core.AbstractAWSClientFactory;
import org.springframework.util.Assert;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.AccessControlList;
import com.amazonaws.services.s3.model.AmazonS3Exception;
import com.amazonaws.services.s3.model.CanonicalGrantee;
import com.amazonaws.services.s3.model.EmailAddressGrantee;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.GroupGrantee;
import com.amazonaws.services.s3.model.ListObjectsRequest;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.Permission;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.transfer.TransferManager;
import com.amazonaws.services.s3.transfer.TransferManagerConfiguration;
import com.amazonaws.services.s3.transfer.Upload;
/**
* The default, out of the box implementation of the {@link AmazonS3Operations} that is implemented
* using AWS SDK.
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class DefaultAmazonS3Operations extends AbstractAmazonS3Operations {
private final AWSCredentials credentials;
private AmazonS3Client client;
private volatile TransferManager transferManager; //Used to upload to S3
private volatile ThreadPoolExecutor threadPoolExecutor;
private volatile AbstractAWSClientFactory<AmazonS3Client> s3Factory;
/**
* Constructor
* @param credentials
*/
public DefaultAmazonS3Operations(final AWSCredentials credentials) {
super(credentials);
this.credentials = credentials;
s3Factory = new AbstractAWSClientFactory<AmazonS3Client>() {
@Override
protected AmazonS3Client getClientImplementation() {
String accessKey = credentials.getAccessKey();
String secretKey = credentials.getSecretKey();
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
return new AmazonS3Client(credentials);
}
};
}
@Override
protected void init() {
client = s3Factory.getClient(getAwsEndpoint());
if(threadPoolExecutor == null) {
//Will use the Default Executor,
//See com.amazonaws.services.s3.transfer.internal.TransferManagerUtils for more details
transferManager = new TransferManager(client);
}
else {
transferManager = new TransferManager(client, threadPoolExecutor);
}
//As per amazon it is recommended to use Multi part upload above 100 MB
long multipartUploadThreshold = getMultipartUploadThreshold();
if(multipartUploadThreshold > 0) {
TransferManagerConfiguration config = new TransferManagerConfiguration();
if(multipartUploadThreshold > Integer.MAX_VALUE) {
config.setMultipartUploadThreshold(Integer.MAX_VALUE); //2GB
}
else {
config.setMultipartUploadThreshold((int)multipartUploadThreshold);
}
transferManager.setConfiguration(config);
}
//If none is set, we use the default
}
/**
* The implementation that uses the AWS SDK to list objects from the given bucket
*
* @param bucketName The bucket in which we want to list the objects in
* @param nextMarker The number of objects can be very large and this serves as the marker
* for remembering the last record fetch in the last retrieve operation.
* @param pageSize The max number of records to be retrieved in one list object operation.
* @param prefix The prefix for the list operation, this can serve as the folder whose contents
* are to be listed.
*/
@Override
protected PaginatedObjectsView doListObjects(String bucketName,
String nextMarker, int pageSize, String prefix) {
ListObjectsRequest listObjectsRequest =
new ListObjectsRequest()
.withBucketName(bucketName)
.withPrefix(prefix)
.withMarker(nextMarker);
if(pageSize > 0) {
listObjectsRequest.withMaxKeys(pageSize);
}
ObjectListing listing = client.listObjects(listObjectsRequest);
PaginatedObjectsView view = null;
List<com.amazonaws.services.s3.model.S3ObjectSummary> summaries = listing.getObjectSummaries();
if(summaries != null && !summaries.isEmpty()) {
List<S3ObjectSummary> objectSummaries = new ArrayList<S3ObjectSummary>();
for(final com.amazonaws.services.s3.model.S3ObjectSummary summary:summaries) {
S3ObjectSummary summ = new S3ObjectSummary() {
public long getSize() {
return summary.getSize();
}
public Date getLastModified() {
return summary.getLastModified();
}
public String getKey() {
return summary.getKey();
}
public String getETag() {
return summary.getETag();
}
public String getBucketName() {
return summary.getBucketName();
}
};
objectSummaries.add(summ);
}
view = new PagninatedObjectsViewImpl(objectSummaries,listing.getNextMarker());
}
return view;
}
/**
* Gets the object from the given bucket with the given key using the AWS SDK implementation
*
* @param bucketName
* @param key
* @return The Amazon S3 Object representing the Object in S3, may be null.
*/
@Override
protected AmazonS3Object doGetObject(String bucketName, String key) {
GetObjectRequest request = new GetObjectRequest(bucketName, key);
S3Object s3Object;
try {
s3Object = client.getObject(request);
} catch (AmazonS3Exception e) {
if("NoSuchKey".equals(e.getErrorCode())) {
//If the key is not found, return null rather than throwing the exception
return null;
}
else {
//throw the exception to caller in all other cases
throw e;
}
}
return new AmazonS3Object(s3Object.getObjectMetadata().getUserMetadata(),
s3Object.getObjectMetadata().getRawMetadata(),
s3Object.getObjectContent(),
null);
}
/**
* The implementation puts the given {@link File} instance to the provided bucket against
* the given key.
*
* @param bucketName The bucket on S3 where this object is to be put
* @param key The key against which this Object is to be stored in S3
* @param file resource to be uploaded to S3
* @param objectACL the Object's Access controls for the object to be uploaded
* @param userMetadata The user's metadata to be associated with the object uploaded
* @param stringContentMD5 The MD5 sum of the contents of the file to be uploaded
*/
@Override
public void doPut(String bucketName, String key, File file, AmazonS3ObjectACL objectACL,
Map<String, String> userMetadata,String stringContentMD5) {
ObjectMetadata metadata = new ObjectMetadata();
PutObjectRequest request = new PutObjectRequest(bucketName, key, file);
request.withMetadata(metadata);
if(stringContentMD5 != null) {
metadata.setContentMD5(stringContentMD5);
}
if(userMetadata != null) {
metadata.setUserMetadata(userMetadata);
}
Upload upload;
try {
upload = transferManager.upload(request);
} catch (Exception e) {
throw new AmazonS3OperationException(
credentials.getAccessKey(), bucketName,
key,
"Encountered Exception while invoking upload on multipart/single thread file, " +
"see nested exceptions for more details",
e);
}
//Wait till the upload completes, the call to putObject is synchronous
try {
if(logger.isInfoEnabled()) {
logger.info("Waiting for Upload to complete");
}
upload.waitForCompletion();
if(logger.isInfoEnabled()) {
logger.info("Upload completed");
}
} catch (Exception e) {
throw new AmazonS3OperationException(
credentials.getAccessKey(), bucketName,
key,
"Encountered Exception while uploading the multipart/single thread file, " +
"see nested exceptions for more details",
e);
}
//Now since the object is present on S3, set the AccessControl list on it
//Please note that it is not possible to set the object ACL with the
//put object request, and hence both these operations cannot be atomic
//it is possible the objects is uploaded and the ACl not set due to some
//failure
if(objectACL != null) {
if(logger.isInfoEnabled()) {
logger.info("Setting Access control list for key " + key);
}
try {
client.setObjectAcl(bucketName, key,
getAccessControlList(bucketName, key, objectACL));
} catch (Exception e) {
throw new AmazonS3OperationException(
credentials.getAccessKey(), bucketName,
key,
"Encountered Exception while setting the Object ACL for key , " + key +
"see nested exceptions for more details",
e);
}
if(logger.isDebugEnabled()) {
logger.debug("Successfully set the object ACL");
}
}
}
/**
* Gets the {@link AccessControlList} from the given {@link AmazonS3ObjectACL}
*/
private AccessControlList getAccessControlList(String bucketName,String key,AmazonS3ObjectACL acl) {
AccessControlList accessControlList = null;
if(acl != null) {
if(!acl.getGrants().isEmpty()) {
accessControlList = client.getObjectAcl(bucketName, key);
for(ObjectGrant objGrant:acl.getGrants()) {
Grantee grantee = objGrant.getGrantee();
com.amazonaws.services.s3.model.Grantee awsGrantee;
if(grantee.getGranteeType() == GranteeType.CANONICAL_GRANTEE_TYPE) {
awsGrantee = new CanonicalGrantee(grantee.getIdentifier());
}
else if(grantee.getGranteeType() == GranteeType.EMAIL_GRANTEE_TYPE) {
awsGrantee = new EmailAddressGrantee(grantee.getIdentifier());
}
else {
awsGrantee = GroupGrantee.parseGroupGrantee(grantee.getIdentifier());
if(awsGrantee == null) {
logger.warn("Group grantee with identifier: \"" + grantee.getIdentifier() + "\" not found. skipping this grant");
continue;
}
}
ObjectPermissions perm = objGrant.getPermission();
Permission permission;
if(perm == ObjectPermissions.READ) {
permission = Permission.Read;
}
else if(perm == ObjectPermissions.READ_ACP) {
permission = Permission.ReadAcp;
}
else
permission = Permission.WriteAcp;
accessControlList.grantPermission(awsGrantee, permission);
}
}
}
return accessControlList;
}
/**
* Gets the thread pool executor that will be used to upload the object in multiparts
* concurrently
*/
public ThreadPoolExecutor getThreadPoolExecutor() {
return threadPoolExecutor;
}
/**
* Used only when we upload the data using multi part upload. The thread pool will be used
* to upload the data concurrently
*
* @param threadPoolExecutor May not be null
*/
public void setThreadPoolExecutor(ThreadPoolExecutor threadPoolExecutor) {
Assert.notNull(threadPoolExecutor, "'threadPoolExecutor' is null");
this.threadPoolExecutor = threadPoolExecutor;
}
}

View File

@@ -1,103 +0,0 @@
/*
* 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.s3.core;
/**
* Indicates the Grantee who is being given the Access to a particular resource on S3
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class Grantee {
private String identifier;
private GranteeType granteeType;
public Grantee() {
}
public Grantee(String identifier, GranteeType granteeType) {
this.identifier = identifier;
this.granteeType = granteeType;
}
/**
* The identifier of a particular type identifying the grantee
* @param id
*/
void setIdentifier(String identifier) {
this.identifier = identifier;
}
/**
* Gets the particular identifier representing the grantee
*/
public String getIdentifier() {
return identifier;
}
/**
* Gets the Particular grantee Type
*/
public GranteeType getGranteeType() {
return granteeType;
}
/**
* Sets the Type of the grantee see {@link GranteeType} for more information
* @param granteeType
*/
public void setGranteeType(GranteeType granteeType) {
this.granteeType = granteeType;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((granteeType == null) ? 0 : granteeType.hashCode());
result = prime * result
+ ((identifier == null) ? 0 : identifier.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Grantee other = (Grantee) obj;
if (granteeType != other.granteeType)
return false;
if (identifier == null) {
if (other.identifier != null)
return false;
} else if (!identifier.equals(other.identifier))
return false;
return true;
}
}

View File

@@ -1,45 +0,0 @@
/*
* 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.s3.core;
/**
* Identifies the type of the grantee. E.g. A grantee can be identified using the canonical
* identifier or the email id.
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public enum GranteeType {
/**
* This represents the Canonical id of the users AWS account
*/
CANONICAL_GRANTEE_TYPE,
/**
* This email is usually resolved to the canonical id of the user.
* This would fail and an error would be thrown if more than 2 accounts are
* related to the user's email account.
*/
EMAIL_GRANTEE_TYPE,
/**
* These represent come constants representing some predefined groups by Amazon S3
*/
GROUP_GRANTEE_TYPE;
}

View File

@@ -1,47 +0,0 @@
/*
* 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.s3.core;
/**
* Various types of Groups who can be granted permissions on amazon S3 objects
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public enum GroupGranteeType {
/**
* To grants anonymous access to all objects in the bucket
*/
AllUsers("http://acs.amazonaws.com/groups/global/AllUsers"),
/**
* To grant access to all authenticated users of AWS who is logged in using
* their AWS credentials
*/
AuthenticatedUsers("http://acs.amazonaws.com/groups/global/AuthenticatedUsers");
private String identifier;
private GroupGranteeType(String identifier) {
this.identifier = identifier;
}
public String getIdentifier() {
return identifier;
}
}

View File

@@ -1,87 +0,0 @@
/*
* 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.s3.core;
/**
* Represent one Grant for a Grantee and the associated permissions
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class ObjectGrant {
private final Grantee grantee;
private final ObjectPermissions permission;
/**
* Instantiate an Object grant for the given grantee and with given permissions
* @param grantee
* @param permission
*/
public ObjectGrant(Grantee grantee, ObjectPermissions permission) {
super();
this.grantee = grantee;
this.permission = permission;
}
/**
* Gets the grantee for this particular object permission
*/
public Grantee getGrantee() {
return grantee;
}
/**
* Gets the Corresponding object permission
*/
public ObjectPermissions getPermission() {
return permission;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((grantee == null) ? 0 : grantee.hashCode());
result = prime * result
+ ((permission == null) ? 0 : permission.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ObjectGrant other = (ObjectGrant) obj;
if (grantee == null) {
if (other.grantee != null)
return false;
} else if (!grantee.equals(other.grantee))
return false;
if (permission != other.permission)
return false;
return true;
}
}

View File

@@ -1,44 +0,0 @@
/*
* 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.s3.core;
/**
* Represents the various types of permissions on the object in S3
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public enum ObjectPermissions {
/**
* Indicates the grantee has permissions to read the object from the containing bucket
*/
READ,
/**
* Indicates the grantee has permissions to read the Access control permissions of the
* Object in S3
*/
READ_ACP,
/**
* Indicates the grantee has permissions to write the Access control permissions of the
* Object in S3
*/
WRITE_ACP;
}

View File

@@ -1,50 +0,0 @@
/*
* 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.s3.core;
import java.util.List;
/**
* Returns the Paginated view of the objects in Amazon S3 for the queries bucket
* See {@link AmazonS3Operations} for more details on various operations on S3
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public interface PaginatedObjectsView {
/**
* Gets the Paginated List of Object names
* @return A {@link List} of paginated object names
*/
List<S3ObjectSummary> getObjectSummary();
/**
* Invoke this method to know if more pages of results is present
* @return true if more results are present in the {@link List} of objects returned
*/
boolean hasMoreResults();
/**
* Contains the marker that can be used to get the next listing of objects from the
* S3. Contains a null value if the listing is complete, the hasMoreResults
* method will return true if this marker contains a non null value.
*/
String getNextMarker();
}

View File

@@ -1,54 +0,0 @@
/*
* 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.s3.core;
import java.util.Date;
/**
* The summary of the Object stored on Amazon S3.
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public interface S3ObjectSummary {
/**
* Gets the Bucket nane in which the object is kept on S3
*/
String getBucketName();
/**
* Gets the keys under which the Object is stored on S3
*/
String getKey();
/**
* Gets the Hex encoded 128 bit MD5 digest of the contents of the object uploaded on S3
*/
String getETag();
/**
* Gets the size of the object in bytes
*/
long getSize();
/**
* Gets the Date the object was last modified
*/
Date getLastModified();
}

View File

@@ -1,51 +0,0 @@
/*
* 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 0.5
*
*/
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);
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2002-2014 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.messaging.MessageHandler;
/**
* parse the &lt;ses-outbound-channel-adapter/&gt; of the "int-aws" namespace
*
* @author Amol Nayak
* @author Rob Harrop
*
* @since 0.5
*
*/
public class AmazonSESOutboundAdapterParser extends
AbstractAWSOutboundChannelAdapterParser {
@Override
public Class<? extends MessageHandler> getMessageHandlerImplementation() {
return AmazonSESMessageHandler.class;
}
}

View File

@@ -1,94 +0,0 @@
/*
* 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 0.5
*
*/
public class AmazonSESMailSendException extends AWSOperationException {
/**
*
*/
private static final long serialVersionUID = -3035267174544370619L;
private final Map<Object, Exception> failedMessages;
/**
*
* @param accessKey
* @param message
* @param cause
* @param failedMessages
*/
public AmazonSESMailSendException(String accessKey, String message,
Throwable cause,Map<Object, Exception> failedMessages) {
super(accessKey, message, cause);
this.failedMessages = failedMessages;
}
/**
*
* @param accessKey
* @param message
* @param failedMessages
*/
public AmazonSESMailSendException(String accessKey,
String message,Map<Object, Exception> failedMessages) {
super(accessKey, message);
this.failedMessages = failedMessages;
}
/**
*
* @param accessKey
* @param cause
* @param failedMessages
*/
public AmazonSESMailSendException(String accessKey,
Throwable cause,Map<Object, Exception> failedMessages) {
super(accessKey, cause);
this.failedMessages = failedMessages;
}
/**
*
* @param accessKey
* @param failedMessages
*/
public AmazonSESMailSendException(String accessKey,Map<Object, Exception> 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
*/
public Map<Object, Exception> getFailedMessages() {
return failedMessages;
}
}

View File

@@ -1,121 +0,0 @@
/*
* 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.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.services.simpleemail.AWSJavaMailTransport;
/**
* The implementation class for sending mail using the Amazon SES service
*
* @author Amol Nayak
* @author Gunnar Hillert
*
* @since 0.5
*
*/
public class DefaultAmazonSESMailSender implements JavaMailSender {
public DefaultAmazonSESMailSender(AWSCredentials credentials) {
if(credentials == null) {
throw new AWSOperationException(null, "Credentials cannot be null, provide a non null valid set of 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);
}
private JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl() {
@Override
protected Transport getTransport(Session session)
throws NoSuchProviderException {
return new AWSJavaMailTransport(session, null);
}
};
@Override
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 {
return javaMailSender.createMimeMessage(contentStream);
}
@Override
public void send(MimeMessage mimeMessage) throws MailException {
javaMailSender.send(mimeMessage);
}
@Override
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);
}
}

View File

@@ -1 +0,0 @@
http\://www.springframework.org/schema/integration/aws=org.springframework.integration.aws.config.xml.AWSNamespaceHandler

View File

@@ -1,2 +0,0 @@
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

View File

@@ -1,3 +0,0 @@
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

View File

@@ -1,391 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.springframework.org/schema/integration/aws"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:integration="http://www.springframework.org/schema/integration"
targetNamespace="http://www.springframework.org/schema/integration/aws"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<xsd:import namespace="http://www.springframework.org/schema/tool"/>
<xsd:import namespace="http://www.springframework.org/schema/integration"
schemaLocation="http://www.springframework.org/schema/integration/spring-integration.xsd"/>
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines the configuration elements for Spring Integration's AWS Adapters.
]]></xsd:documentation>
</xsd:annotation>
<xsd:attributeGroup name="awsAdaptersCommonAttributes">
<xsd:attribute name="accessKey" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Specify the access key to be used to connect to the AWS service.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="secretKey" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Specify the secret key corresponding to the access key to be used to
authenticate the user and connect to the AWS service.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="propertiesFile" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
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.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="credentials-ref" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.aws.core.AWSCredentials"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
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
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:attributeGroup>
<xsd:element name="ses-outbound-channel-adapter">
<xsd:complexType>
<xsd:annotation>
<xsd:documentation>
Defines an outbound mail-sending Channel Adapter for sending mails using Amazon SES.
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
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.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="awsAdaptersCommonAttributes"/>
<xsd:attribute name="auto-startup" type="xsd:string" default="true">
<xsd:annotation>
<xsd:documentation>
Lifecycle attribute signaling if this component should be started during
Application Context startup. Default is 'true'
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="order">
<xsd:annotation>
<xsd:documentation>
Specifies the order for invocation when this endpoint is connected as a
subscriber to a SubscribableChannel.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="s3-outbound-channel-adapter">
<xsd:complexType>
<xsd:annotation>
<xsd:documentation>
Defines an outbound S3 Channel Adapter for Uploading files to Amazon S3
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="awsAdaptersCommonAttributes"/>
<xsd:attribute name="bucket" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>
The mandatory attribute that would be used to provide the AWS bucket to which
the objects needs to be uploaded.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="charset" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Relevant only when the payload of the message to the outbound adapter is of
type java.lang.String. The default charset is UTF-8.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="multipart-upload-threshold" type="xsd:integer">
<xsd:annotation>
<xsd:documentation>
Using 'Amazon Multipart Upload' you can upload data as a set of parts using
parallel threads. This non-negative integer value representing bytes which is used
to provide the threshold after which the upload to the S3 bucket will be done
using 'Amazon Multipart Upload'. Amazon recommends the size to be 100 MB.
The minimum threshold for 'Amazon Multipart Upload' is 5120 bytes.
If the attribute is not specified, then the value used is the default value used
by the underlying implementation. The default implementation uses AWS SDK which
uses Multi part upload after 16 MB. The maximum value for this is 2 GB. Any value
greater than 2 GB will not throw an exception but the value will be set to
2 GB internally for the threshold.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="temporary-directory" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
If the payload of the message is an InputStream, byte[] or String
the contents are written to a temporary file in the provided temporary directory
location before being uploaded to the S3. In absence of this attribute, the
value is defaulted to the value of the system property "java.io.tmpdir"
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="temporary-suffix" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
The suffix for the files if a temporary file is to be generated. The value
defaults to ".writing"
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="thread-pool-executor" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="java.util.concurrent.ThreadPoolExecutor"/>
</tool:annotation>
<xsd:documentation>
The thread pool executor to be used for multi part uploads.
If none is provided, the default one used by the underlying SDK
or library will be used.
</xsd:documentation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="remote-directory" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
The String literal that gives the remote folder in the provided bucket where
the files will be uploaded. This attribute is mutually exclusive to
remote-directory-expression
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="remote-directory-expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
This attribute is mutually exclusive with the remote-directory attribute
and is used to provide an expression that would be evaluated against the incoming
message to derive the remote directory name in the given bucket.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="file-name-generator" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.aws.s3.FileNameGenerationStrategy"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
The instance that would be used to generate the name of the file that would be
stored in S3. If none is specified then
org.springframework.integration.aws.s3.DefaultFileNameGenerationStrategy would be used.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="file-name-generation-expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
The filename generation expression that is mutually exclusive to the
file-name-generator attribute. The default expression is "headers['file_name']"
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="auto-startup" type="xsd:string" default="true"/>
<xsd:attribute name="order">
<xsd:annotation>
<xsd:documentation>
Specifies the order for invocation when this endpoint is connected as a
subscriber to a SubscribableChannel.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="s3-operations" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:expected-type type="org.springframework.integration.aws.s3.core.AmazonS3Operations"/>
</xsd:appinfo>
<xsd:documentation>
Reference to the bean with an implementation of org.springframework.integration.aws.s3.core.AmazonS3Operations
that would be used to perform the operations on the S3 bucket. If not provided, the
default implementation used is
org.springframework.integration.aws.s3.core.DefaultAmazonS3Operations which is the
implementation using the AWS SDK.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="aws-endpoint" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
The String that gives the endpoint to use for the adapter, if none is
specified the default used is s3.amazonaws.com.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="s3-inbound-channel-adapter">
<xsd:complexType>
<xsd:annotation>
<xsd:documentation>
Defines the inbound channel adapter for Amazon S3. The component is used to synchronize
the objects in an S3 bucket with the file system.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence minOccurs="0" maxOccurs="1">
<xsd:element ref="integration:poller"/>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="awsAdaptersCommonAttributes"/>
<xsd:attribute name="bucket" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>
The mandatory attribute that would be used to provide the AWS bucket to which
the objects needs to be uploaded.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="temporary-suffix" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
The suffix files will use while the are being written to the local file system.
A file present with this suffix on the local file system denotes that the file
is not completely received from the s3 bucket.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="s3-operations" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:expected-type type="org.springframework.integration.aws.s3.core.AmazonS3Operations"/>
</xsd:appinfo>
<xsd:documentation>
Reference to the bean with an implementation of org.springframework.integration.aws.s3.core.AmazonS3Operations
that would be used to perform the operations on the S3 bucket. If not provided, the
default implementation used is
org.springframework.integration.aws.s3.core.DefaultAmazonS3Operations which uses AWS SDK.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="aws-endpoint" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
The String that gives the endpoint to use for the adapter, if none is
specified the default used is s3.amazonaws.com.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="remote-directory" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
This is the sub folder if any on the remote bucket that would be synchronized
with the local directory. Useful if a part of the bucket is to be synchronized.
If none specified, the entire bucket will be synchronized with the local directory.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="local-directory" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
The attribute specifying the directory on the local file system where
the objects from S3 bucket would be synchronized to. Either of local-directory
or the local-directory-expression are mandatory.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="local-directory-expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
The attribute specifying the expression to find the directory on the local file
system where the objects from S3 bucket would be synchronized to. Either of local-directory
or the locl-directory-expression are mandatory.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="auto-startup" type="xsd:string" default="true"/>
<xsd:attribute name="max-objects-per-batch" type="xsd:integer">
<xsd:annotation>
<xsd:documentation>
The maximum number of objects returned in the listOperation performed on the S3 bucket
The default value used internally is 100. That is not more than 100 objects would be returned
in one call to list the objects, subsequent calls will be made to the Web service to retrieve the next
batch of objects.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="accept-sub-folders" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
The boolean value that would be used to specify if the sub folders of the given
folder are to be synchronized or not. By default the value is false and only files
at the level of the specified folder (or root of the bucket if no remote folder specified)
are synchronized. The objects in the sub folder are ignored.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="file-name-wildcard" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
The wildcard pattern that would be used to further filter out the objects listed.
This attribute is mutually exclusive to the file-name-regex attribute.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="file-name-regex" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
The regex that would be used to further filter out the objects listed.
This attribute is mutually exclusive to the file-name-wildcard attribute.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:schema>

View File

@@ -1,60 +0,0 @@
/*
* 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 org.junit.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 0.5
*
*/
public class AWSClientFactoryTest {
AbstractAWSClientFactory<AmazonSQSClient> factory = new AbstractAWSClientFactory<AmazonSQSClient>() {
@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);
}
}

View File

@@ -1,165 +0,0 @@
/*
* 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 java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;
import org.apache.commons.codec.binary.Base64;
import org.springframework.integration.aws.core.AWSCredentials;
import org.springframework.integration.aws.core.PropertiesAWSCredentials;
/**
* Common test class utility to be used by AmazonWS test cases
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public final class AWSTestUtils {
private static PropertiesAWSCredentials credentials;
private AWSTestUtils() {
throw new AssertionError("Cannot instantiate a utility class");
}
/**
* Method that will be used to test the contents of the file to assert we are getting the
* the right value
*
* @param permFile
* @param expectedContent
* @throws IOException
*/
public static final void assertFileContent(File permFile, String expectedContent)
throws IOException {
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(permFile)));
Assert.assertEquals(expectedContent, reader.readLine());
} finally {
if(reader != null) {
reader.close();
}
}
}
/**
* Writes the content to the file at given location.
*
* @param path
* @param content
*/
public static final void writeToFile(String path, String content) throws IOException {
//create the required directories if needed
if(path.contains(File.separator)) {
int index = path.lastIndexOf(File.separatorChar);
if(index != 0) {
new File(path.substring(0, index)).mkdirs();
}
}
File file = new File(path);
FileOutputStream fos = new FileOutputStream(file);
fos.write(content.getBytes());
fos.close();
}
/**
* The static helper method that would be used by other AWS tests to
* get the implementation of the {@link AWSCredentials} instance
* @throws Exception
*/
public static AWSCredentials getCredentials() {
if(credentials == null) {
credentials =
new PropertiesAWSCredentials("classpath:awscredentials.properties");
try {
credentials.afterPropertiesSet();
} catch (Exception e) {
return null;
}
}
return credentials;
}
/**
* When passed a {@link File} instance for the root directory, the contents are recursively
* checked and the listing of the directories is retrieved.
*
* @param rootDirectory
*/
public static List<File> getContentsRecursively(File rootDirectory) {
if(!rootDirectory.isDirectory()) {
return null;
}
else {
List<File> files = new ArrayList<File>();
getContentsRecursively(rootDirectory, files);
return files;
}
}
/**
* Recursively gets all the files present under the provided directory
* @param file
* @param files
*/
private static void getContentsRecursively(File file, List<File> files) {
if(file.isFile()) {
files.add(file);
}
else if(file.isDirectory()){
File[] children = file.listFiles();
if(children != null && children.length != 0) {
for(File child:children) {
getContentsRecursively(child, files);
}
}
}
}
/**
* Helper method that will be used to generate the base64 encoded MD5 hash of the string
*
* @param input
*/
public static String md5Hash(String input) {
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
byte[] digestedBytes = digest.digest(input.getBytes("UTF-8"));
return new String(Base64.encodeBase64(digestedBytes),"UTF-8");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
} catch(UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
}

View File

@@ -1,51 +0,0 @@
/*
* 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 0.5
*
*/
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;
}
}

View File

@@ -1,134 +0,0 @@
/*
* Copyright 2002-2014 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.junit.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.endpoint.AbstractEndpoint;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.MessageHandler;
/**
* The Abstract test class for the AWS outbound adapter parsers
*
* @author Amol Nayak
* @author Rob Harrop
*
* @since 0.5
*
*/
public abstract class AbstractAWSOutboundChannelAdapterParserTests<T extends MessageHandler> {
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
*/
@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
*
*/
protected abstract String getConfigFilePath();
/**
* The subclass should return the {@link AWSCredentials} instance that is being used
* to configure the adapters
*
*/
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());
}
}

View File

@@ -1,152 +0,0 @@
/*
* 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 org.junit.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 0.5
*
*/
public abstract class AbstractAWSClientFactoryTests<T extends AmazonWebServiceClient> {
protected AbstractAWSClientFactory<T> factory;
@Before
public final void setup() {
factory = getFactory();
}
/**
* The subclass is responsible for returning the appropriate factory implementation
*/
protected abstract AbstractAWSClientFactory<T> getFactory();
/**
* Gets the endpoint for the service in US-EAST-1 region
*/
protected abstract String getUSEast1Endpoint();
/**
* Gets the endpoint for the service in US-EAST-1 region
*/
protected abstract String getEUWest1Endpoint();
/**
* Gets the Suffix for the endpoint URL which is service specific
*/
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<String, T> 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<String, T> 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<String, T> 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<String, T> 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());
}
}

View File

@@ -1,273 +0,0 @@
/*
* Copyright 2002-2014 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.s3;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.springframework.integration.aws.common.AWSTestUtils.md5Hash;
import static org.springframework.integration.aws.s3.AmazonS3OperationsMockingUtil.mockAmazonS3Operations;
import static org.springframework.integration.aws.s3.AmazonS3OperationsMockingUtil.mockS3Operations;
import static org.springframework.integration.test.util.TestUtils.getPropertyValue;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.integration.aws.core.BasicAWSCredentials;
import org.springframework.integration.aws.s3.core.AbstractAmazonS3Operations;
import org.springframework.integration.aws.s3.core.AmazonS3Object;
import org.springframework.integration.aws.s3.core.AmazonS3ObjectACL;
import org.springframework.integration.aws.s3.core.AmazonS3Operations;
import org.springframework.integration.aws.s3.core.PaginatedObjectsView;
import org.springframework.messaging.Message;
/**
* The test class for {@link AmazonS3InboundSynchronizationMessageSource}
*
* @author Amol Nayak
* @author Rob Harrop
*
* @since 0.5
*
*/
public class AmazonS3InboundSynchronizationMessageSourceTests {
@Rule
public TemporaryFolder temp = new TemporaryFolder();
private static AmazonS3Operations operations;
@BeforeClass
public static void setup() {
operations = mockS3Operations();
}
/**
* Tests by providing null credentials.
*/
@Test(expected = IllegalArgumentException.class)
public void withNullCredentials() {
AmazonS3InboundSynchronizationMessageSource src = new AmazonS3InboundSynchronizationMessageSource();
src.setCredentials(null);
}
/**
* Tests by providing null temporary suffix
*/
@Test(expected = IllegalArgumentException.class)
public void withNullTempSuffix() {
AmazonS3InboundSynchronizationMessageSource src = new AmazonS3InboundSynchronizationMessageSource();
src.setTemporarySuffix(null);
}
/**
* Tests by providing null wildcard
*/
@Test(expected = IllegalArgumentException.class)
public void withNullWildcard() {
AmazonS3InboundSynchronizationMessageSource src = new AmazonS3InboundSynchronizationMessageSource();
src.setFileNameWildcard(null);
}
/**
* Tests by providing null regex
*/
@Test(expected = IllegalArgumentException.class)
public void withNullRegex() {
AmazonS3InboundSynchronizationMessageSource src = new AmazonS3InboundSynchronizationMessageSource();
src.setFileNameRegex(null);
}
/**
* Tests by providing both regex and wildcard
*/
@Test(expected = IllegalArgumentException.class)
public void withBothRegexAndWildcard() {
AmazonS3InboundSynchronizationMessageSource src = new AmazonS3InboundSynchronizationMessageSource();
src.setFileNameRegex("[a-z]+\\.txt");
src.setFileNameWildcard("*.txt");
}
/**
* Tests by providing both wildcard and regex, unlike previous one, this sets the wildcard first
*/
@Test(expected = IllegalArgumentException.class)
public void withBothWildcardAndRegex() {
AmazonS3InboundSynchronizationMessageSource src = new AmazonS3InboundSynchronizationMessageSource();
src.setFileNameWildcard("*.txt");
src.setFileNameRegex("[a-z]+\\.txt");
}
/**
* Tests providing null remote directory
*/
@Test(expected = IllegalArgumentException.class)
public void withNullRemoteDirectory() {
AmazonS3InboundSynchronizationMessageSource src = new AmazonS3InboundSynchronizationMessageSource();
src.setRemoteDirectory(null);
}
/**
*Tests with a non existent local directory
*/
@Test(expected = IllegalArgumentException.class)
public void withNonExistentLocalDirectory() {
AmazonS3InboundSynchronizationMessageSource src = new AmazonS3InboundSynchronizationMessageSource();
src.setDirectory(new LiteralExpression("SomeNotExistentDir"));
src.setBeanFactory(Mockito.mock(BeanFactory.class));
src.afterPropertiesSet();
}
/**
* Tests with a {@link File} instance that is not a directory
* @throws IOException
*/
@Test(expected = IllegalArgumentException.class)
public void withNonDirectory() throws IOException {
File file = temp.newFile("SomeFile.txt");
AmazonS3InboundSynchronizationMessageSource src = new AmazonS3InboundSynchronizationMessageSource();
src.setDirectory(new LiteralExpression(file.getAbsolutePath()));
src.setBeanFactory(Mockito.mock(BeanFactory.class));
src.afterPropertiesSet();
}
/**
* Tests with a null s3 operation.
*/
@Test(expected = IllegalArgumentException.class)
public void withNullS3Operations() {
AmazonS3InboundSynchronizationMessageSource src = new AmazonS3InboundSynchronizationMessageSource();
src.setS3Operations(null);
}
/**
* Doesn't set the {@link AmazonS3Operations} instance and relies on the default one.
* sets the temp suffix and the thread pool executor.
*/
@Test
public void withDefaultS3Service() {
AmazonS3InboundSynchronizationMessageSource src = new AmazonS3InboundSynchronizationMessageSource();
BasicAWSCredentials credentials = new BasicAWSCredentials("dummy", "dummy");
src.setTemporarySuffix(".temp");
src.setCredentials(credentials);
src.setDirectory(new LiteralExpression(temp.getRoot().getAbsolutePath()));
src.setBeanFactory(Mockito.mock(BeanFactory.class));
src.afterPropertiesSet();
assertEquals(".temp", getPropertyValue(src, "s3Operations.temporaryFileSuffix", String.class));
}
/**
* Instantiates with a custom implementation of {@link AmazonS3Operations}
* which extends from {@link AbstractAmazonS3Operations}. Also sets the following
* s3Operations, directory, fileNameRegex, remoteDirectory, maxObjectsPerBatch and temporarySuffix
* attributes.
*/
@Test
public void withCustomS3Operations() {
BasicAWSCredentials credentials = new BasicAWSCredentials("dummy", "dummy");
AbstractAmazonS3Operations ops = new AbstractAmazonS3Operations(credentials) {
@Override
protected void doPut(String bucket, String key, File file,
AmazonS3ObjectACL objectACL, Map<String, String> userMetadata,
String stringContentMD5) {
}
@Override
protected PaginatedObjectsView doListObjects(String bucketName,
String nextMarker, int pageSize, String prefix) {
return null;
}
@Override
protected AmazonS3Object doGetObject(String bucketName, String key) {
return null;
}
};
AmazonS3InboundSynchronizationMessageSource src = new AmazonS3InboundSynchronizationMessageSource();
src.setS3Operations(ops);
src.setBucket("testbucket");
src.setDirectory(new LiteralExpression(temp.getRoot().getAbsolutePath()));
src.setFileNameRegex("[a-z]+\\.txt");
src.setRemoteDirectory("remotedirectory");
src.setMaxObjectsPerBatch(15);
src.setTemporarySuffix(".temp");
src.setAcceptSubFolders(true);
src.setDirectory(new LiteralExpression(temp.getRoot().getAbsolutePath()));
src.setBeanFactory(Mockito.mock(BeanFactory.class));
src.afterPropertiesSet();
assertEquals(ops, getPropertyValue(src, "s3Operations", AmazonS3Operations.class));
assertEquals(".temp", getPropertyValue(src, "s3Operations.temporaryFileSuffix", String.class));
assertEquals("testbucket", getPropertyValue(src, "bucket", String.class));
assertEquals(temp.getRoot(), getPropertyValue(src, "directory"));
assertEquals("[a-z]+\\.txt", getPropertyValue(src, "synchronizer.fileNameRegex", String.class));
assertEquals(true, getPropertyValue(src, "synchronizer.acceptSubFolders", Boolean.class));
assertEquals(15, getPropertyValue(src, "synchronizer.maxObjectsPerBatch", Integer.class).intValue());
assertEquals("remotedirectory", getPropertyValue(src, "remoteDirectory", String.class));
}
/**
* Synchronizes the local directory to a remote bucket
*/
@Test
public void synchronizeWithLocalDirectory() {
mockAmazonS3Operations(Arrays.asList(
new String[] {"test.txt", "test.txt", md5Hash("test.txt"), null},
new String[] {"sub1/test.txt", "sub1/test.txt", md5Hash("sub1/test.txt"), null},
new String[] {"sub1/sub11/test.txt", "sub1/sub11/test.txt", md5Hash("sub1/sub11/test.txt"), null},
new String[] {"sub2/test.txt", "sub2/test.txt", md5Hash("sub2/test.txt"), null}
));
AmazonS3InboundSynchronizationMessageSource src = new AmazonS3InboundSynchronizationMessageSource();
src.setS3Operations(operations);
src.setBucket(AmazonS3OperationsMockingUtil.BUCKET);
src.setDirectory(new LiteralExpression(temp.getRoot().getAbsolutePath()));
src.setFileNameRegex("[a-z]+\\.txt");
src.setRemoteDirectory("/sub1");
src.setMaxObjectsPerBatch(15);
src.setTemporarySuffix(".temp");
src.setAcceptSubFolders(true);
src.setDirectory(new LiteralExpression(temp.getRoot().getAbsolutePath()));
src.setBeanFactory(Mockito.mock(BeanFactory.class));
src.afterPropertiesSet();
File file = src.receive().getPayload();
assertEquals(temp.getRoot().getAbsoluteFile() +
File.separator + "sub1" + File.separator + "test.txt", file.getAbsolutePath());
file = src.receive().getPayload();
assertEquals(temp.getRoot().getAbsoluteFile() +
File.separator + "sub1" + File.separator + "sub11" + File.separator + "test.txt", file.getAbsolutePath());
Message<File> message = src.receive();
assertNull(message);
}
}

View File

@@ -1,269 +0,0 @@
/*
* Copyright 2002-2014 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.s3;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.springframework.integration.aws.s3.AmazonS3MessageHeaders.FILE_NAME;
import static org.springframework.integration.aws.s3.AmazonS3MessageHeaders.METADATA;
import static org.springframework.integration.aws.s3.AmazonS3MessageHeaders.OBJECT_ACLS;
import static org.springframework.integration.aws.s3.AmazonS3MessageHeaders.USER_METADATA;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.aws.core.BasicAWSCredentials;
import org.springframework.integration.aws.s3.core.AmazonS3Object;
import org.springframework.integration.aws.s3.core.AmazonS3Operations;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandlingException;
/**
* The test class for {@link AmazonS3MessageHandler}, we rely on mock of {@link AmazonS3Operations}
* to test the behavior.
*
* @author Amol Nayak
* @author Rob Harrop
*
* @since 0.5
*
*/
public class AmazonS3MessageHandlerTests {
private static AmazonS3Operations operations;
private static PutObjectParameterHolder holder = new PutObjectParameterHolder();
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
@BeforeClass
public static void setup() {
operations = Mockito.mock(AmazonS3Operations.class);
doAnswer(new Answer<Object>() {
public Object answer(InvocationOnMock inv) {
Object[] args = inv.getArguments();
holder.setBucket((String)args[0]);
holder.setFolder((String)args[1]);
holder.setObjectName((String)args[2]);
holder.setS3Object((AmazonS3Object)args[3]);
return null;
}
}).
when(operations)
.putObject(anyString(), anyString(), anyString(), any(AmazonS3Object.class));
}
private AmazonS3MessageHandler getHandler() {
AmazonS3MessageHandler handler = new AmazonS3MessageHandler(new BasicAWSCredentials(), operations);
//set the remote directory to root by default
handler.setRemoteDirectoryExpression(new LiteralExpression("/"));
handler.setBucket("TestBucket");
handler.setBeanFactory(Mockito.mock(BeanFactory.class));
handler.afterPropertiesSet();
return handler;
}
private static class PutObjectParameterHolder {
private String bucket;
private String folder;
private String objectName;
private AmazonS3Object s3Object;
public String getBucket() {
return bucket;
}
public void setBucket(String bucket) {
this.bucket = bucket;
}
public String getFolder() {
return folder;
}
public void setFolder(String folder) {
this.folder = folder;
}
public String getObjectName() {
return objectName;
}
public void setObjectName(String objectName) {
this.objectName = objectName;
}
public AmazonS3Object getS3Object() {
return s3Object;
}
public void setS3Object(AmazonS3Object s3Object) {
this.s3Object = s3Object;
}
}
/**
* Tests with a message payload of type {@link String}
*/
@Test
public void withStringPayload() {
Message<String> message = MessageBuilder.withPayload("Test String").build();
AmazonS3MessageHandler handler = getHandler();
handler.handleMessage(message);
AmazonS3Object object = holder.getS3Object();
Assert.assertNotNull(object.getInputStream());
Assert.assertNull(object.getFileSource());
assertCommonValues(message,object);
}
/**
* Tests with a message with payload of type {@link InputStream}
*/
@Test
public void withInputStreamPayload() {
InputStream bin = new ByteArrayInputStream("SomeString".getBytes());
Message<InputStream> message = MessageBuilder.withPayload(bin).build();
AmazonS3MessageHandler handler = getHandler();
handler.handleMessage(message);
AmazonS3Object object = holder.getS3Object();
Assert.assertNotNull(object.getInputStream());
Assert.assertNull(object.getFileSource());
assertCommonValues(message,object);
}
/**
* Tests with a message with payload of type byte[]
*/
@Test
public void withByteArrayPayload() {
Message<byte[]> message = MessageBuilder.withPayload("String".getBytes()).build();
AmazonS3MessageHandler handler = getHandler();
handler.handleMessage(message);
AmazonS3Object object = holder.getS3Object();
Assert.assertNotNull(object.getInputStream());
Assert.assertNull(object.getFileSource());
assertCommonValues(message,object);
}
/**
* Tests with a message with payload of type {@link File} which is a file with temporary suffix
*/
@Test
public void withTempFileTypePayload() throws Exception {
final File file = tempFolder.newFile("TempFile.txt.writing");
messageWithFileTypePayload(file);
}
/**
* Tests with a message with payload of type {@link File} which is a file without temporary suffix
*/
@Test
public void withFileTypePayload() throws Exception {
final File file = tempFolder.newFile("TempFile.txt");
messageWithFileTypePayload(file);
}
/**
*Test case to with message of an incompatible type, {@link Integer} in this case.
*
*/
@Test(expected=MessageHandlingException.class)
public void withIncompatiblePayload() {
Message<Integer> message = MessageBuilder.withPayload(1).build();
AmazonS3MessageHandler handler = getHandler();
handler.handleMessage(message);
}
/**
* Tests with all the header provided in the message
*/
@Test
public void withAllHeaders() {
Map<String, Collection<String>> acls = new HashMap<String, Collection<String>>();
acls.put("test@test.com", Arrays.asList("Read", "Write acp"));
Message<String> message = MessageBuilder.withPayload("Test Content")
.setHeader(FILE_NAME, "TestFileName.txt")
.setHeader(USER_METADATA, Collections.singletonMap("UserMD", "UserMD"))
.setHeader(METADATA, Collections.singletonMap("Metadata", "Metadata"))
.setHeader(OBJECT_ACLS, acls)
.setHeader("remoteDirectory", "/remote")
.build();
AmazonS3MessageHandler handler = getHandler();
SpelExpressionParser parser = new SpelExpressionParser();
handler.setRemoteDirectoryExpression(parser.parseExpression("headers['remoteDirectory']"));
handler.handleMessage(message);
Assert.assertEquals("TestBucket", holder.getBucket());
Assert.assertEquals("TestFileName.txt", holder.getObjectName());
Assert.assertEquals("/remote", holder.getFolder());
AmazonS3Object object = holder.getS3Object();
Assert.assertNotNull(object);
Assert.assertNotNull(object.getInputStream());
Assert.assertNotNull(object.getMetaData());
Assert.assertNotNull(object.getUserMetaData());
Assert.assertNotNull(object.getObjectACL());
Assert.assertEquals(2,object.getObjectACL().getGrants().size());
}
/**
* The common method to test messages with payload of type {@link File}
* @param file
*/
private void messageWithFileTypePayload(File file) throws Exception {
file.createNewFile();
Message<File> message = MessageBuilder.withPayload(file).build();
AmazonS3MessageHandler handler = getHandler();
handler.handleMessage(message);
AmazonS3Object object = holder.getS3Object();
Assert.assertEquals("TempFile.txt", holder.getObjectName());
Assert.assertNotNull(object.getFileSource());
Assert.assertNull(object.getInputStream());
Assert.assertNull(object.getMetaData());
Assert.assertNull(object.getObjectACL());
Assert.assertNull(object.getUserMetaData());
file.delete();
}
/**
* The method used to assert the values for tests with String, InputStream and byte[] parameters
* @param message
*/
private void assertCommonValues(Message<?> message,AmazonS3Object object) {
Assert.assertEquals(message.getHeaders().getId().toString() + ".ext", holder.getObjectName());
Assert.assertEquals("/", holder.getFolder());
Assert.assertEquals("TestBucket", holder.getBucket());
Assert.assertNotNull(object);
Assert.assertNull(object.getMetaData());
Assert.assertNull(object.getObjectACL());
Assert.assertNull(object.getUserMetaData());
}
}

View File

@@ -1,264 +0,0 @@
/*
* 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.s3;
import static org.springframework.integration.aws.s3.core.ObjectPermissions.READ;
import static org.springframework.integration.aws.s3.core.ObjectPermissions.READ_ACP;
import static org.springframework.integration.aws.s3.core.ObjectPermissions.WRITE_ACP;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.integration.aws.s3.core.AmazonS3Object;
import org.springframework.integration.aws.s3.core.AmazonS3ObjectACL;
import org.springframework.integration.aws.s3.core.Grantee;
import org.springframework.integration.aws.s3.core.GranteeType;
import org.springframework.integration.aws.s3.core.ObjectGrant;
import org.springframework.integration.aws.s3.core.ObjectPermissions;
import org.springframework.integration.test.util.TestUtils;
/**
* The test case for the class {@link AmazonS3ObjectBuilder}
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class AmazonS3ObjectBuilderTests {
@Rule
public TemporaryFolder temp = new TemporaryFolder();
private static final String GROUP_GRANTEE = "http://acs.amazonaws.com/groups/global/AllUsers";
private static final String EMAIL_GRANTEE = "test@test.com";
private static final String CANONICAL_GRANTEE = "12345678900987654321abcdefabcdeab12345678900987654321abcdefabcde";
/**
* Tries to construct the object with a null file instance
*/
@Test(expected=IllegalArgumentException.class)
public void withNullFile() {
AmazonS3ObjectBuilder.getInstance().fromFile(null);
}
/**
* Tries to construct the file from a non existent file
*/
@Test(expected=IllegalArgumentException.class)
public void withNonExistentFile() {
AmazonS3ObjectBuilder.getInstance().fromFile(new File("somejunkfile"));
}
/**
* Tries to construct the file which is a directory
*/
@Test(expected=IllegalArgumentException.class)
public void withADirectory() throws IOException {
File dir = temp.newFolder("tempdir");
AmazonS3ObjectBuilder.getInstance().fromFile(dir);
dir.delete();
}
/**
* Tries to construct the {@link AmazonS3Object} from a null location
*/
@Test(expected=IllegalArgumentException.class)
public void withNullPathString() {
AmazonS3ObjectBuilder.getInstance().fromLocation(null);
}
/**
* Tries to construct the {@link AmazonS3Object} from a valid file
*/
@Test
public void withValidFile() throws Exception {
File file = temp.newFile("temp.txt");
String pathname = file.getAbsolutePath();
AmazonS3ObjectBuilder builder =
AmazonS3ObjectBuilder.getInstance().fromLocation(pathname);
file = TestUtils.getPropertyValue(builder, "file", File.class);
Assert.assertNotNull(file);
Assert.assertEquals(pathname, file.getAbsolutePath());
}
/**
* Tries to construct the {@link AmazonS3Object} from an {@link InputStream}
*/
@Test
public void withInputStream()throws Exception {
File tempFile = temp.newFile("Temp.txt");
InputStream in = new FileInputStream(tempFile);
AmazonS3ObjectBuilder builder =
AmazonS3ObjectBuilder.getInstance().fromInputStream(in);
Assert.assertNotNull(TestUtils.getPropertyValue(builder, "in", InputStream.class));
in.close();
}
/**
* Tries to construct the {@link AmazonS3Object} from an {@link InputStream} and a {@link File}
* instance
*/
@Test(expected=IllegalArgumentException.class)
public void withBothinputStreamAndFile() throws Exception {
File tempFile = temp.newFile("Temp.txt");
FileInputStream in = new FileInputStream(tempFile);
AmazonS3ObjectBuilder
.getInstance()
.fromInputStream(in)
.fromFile(tempFile);
in.close();
}
/**
* Constructs the {@link AmazonS3Object} with some user metadata
*/
@Test
public void withUserMetadata() throws Exception {
File tempFile = temp.newFile("Temp.txt");
AmazonS3ObjectBuilder builder = AmazonS3ObjectBuilder
.getInstance()
.fromFile(tempFile)
.withUserMetaData(Collections.singletonMap("Key", "Value"));
Assert.assertNotNull(TestUtils.getPropertyValue(builder, "userMetaData", Map.class));
}
/**
* Constructs the {@link AmazonS3Object} with some metadata
*/
@Test
public void withMetadata() throws Exception {
File tempFile = temp.newFile("Temp.txt");
AmazonS3ObjectBuilder builder = AmazonS3ObjectBuilder
.getInstance()
.fromFile(tempFile)
.withMetaData(Collections.singletonMap("Key", (Object)"Value"));
Assert.assertNotNull(TestUtils.getPropertyValue(builder, "metaData", Map.class));
}
/**
* Constructs the {@link AmazonS3Object} with an invalid ACL identifier
*/
@Test
public void withValidACL() throws Exception {
File tempFile = temp.newFile("Temp.txt");
Map<String, Collection<String>> acls = generateObjectACLS();
AmazonS3ObjectBuilder builder = AmazonS3ObjectBuilder
.getInstance()
.fromFile(tempFile)
.withObjectACL(acls);
AmazonS3ObjectACL acl = TestUtils.getPropertyValue(builder, "objectACL", AmazonS3ObjectACL.class);
assertGrants(acl);
}
/**
* Builds a complete object with all the possible attributes and checks
* if those are properly populated in the constructed {@link AmazonS3Object}
*/
@Test
public void withCompleteObjectFromFile() throws Exception {
File tempFile = temp.newFile("Temp.txt");
Map<String, Collection<String>> acls = generateObjectACLS();
Map<String, String> userMetadata = Collections.singletonMap("Key", "Value");
Map<String, Object> metadata = Collections.singletonMap("Key", (Object)"Value");
AmazonS3ObjectBuilder builder = AmazonS3ObjectBuilder
.getInstance()
.fromFile(tempFile)
.withObjectACL(acls)
.withMetaData(metadata)
.withUserMetaData(userMetadata);
AmazonS3Object object = builder.build();
assertGrants(object.getObjectACL());
Assert.assertEquals(userMetadata, object.getUserMetaData());
Assert.assertEquals(metadata, object.getMetaData());
Assert.assertEquals(tempFile, object.getFileSource());
Assert.assertNull(object.getInputStream());
}
/**
* Builds an object with an {@link InputStream}
*/
@Test
public void wothObjectFromStream() throws Exception {
File tempFile = temp.newFile("Temp.txt");
InputStream in = new FileInputStream(tempFile);
AmazonS3ObjectBuilder builder = AmazonS3ObjectBuilder
.getInstance()
.fromInputStream(in);
AmazonS3Object object = builder.build();
Assert.assertNull(object.getFileSource());
Assert.assertEquals(in,object.getInputStream());
in.close();
}
/**
* Generate the object test ACLS
*/
private Map<String, Collection<String>> generateObjectACLS() {
Map<String, Collection<String>> acls = new HashMap<String, Collection<String>>();
acls.put(CANONICAL_GRANTEE,
Arrays.asList("write acp"));
acls.put(EMAIL_GRANTEE,
Arrays.asList("read acp", "write acp"));
acls.put(GROUP_GRANTEE,
Arrays.asList("read"));
return acls;
}
/**
* Checks for the grants in the object
*
*/
private void assertGrants(AmazonS3ObjectACL acl) {
Set<ObjectGrant> grants = acl.getGrants();
Assert.assertEquals(4, grants.size());
for(ObjectGrant grant:grants) {
Grantee grantee = grant.getGrantee();
GranteeType type = grantee.getGranteeType();
ObjectPermissions permission = grant.getPermission();
if(type == GranteeType.CANONICAL_GRANTEE_TYPE) {
Assert.assertEquals(CANONICAL_GRANTEE, grantee.getIdentifier());
Assert.assertEquals(WRITE_ACP,permission);
}
else if(type == GranteeType.EMAIL_GRANTEE_TYPE){
Assert.assertEquals(EMAIL_GRANTEE, grantee.getIdentifier());
Assert.assertTrue(permission == WRITE_ACP || permission == READ_ACP);
}
else if(type == GranteeType.GROUP_GRANTEE_TYPE) {
Assert.assertEquals(GROUP_GRANTEE, grantee.getIdentifier());
Assert.assertEquals(READ,permission);
}
}
}
}

View File

@@ -1,189 +0,0 @@
/*
* 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.s3;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.integration.aws.s3.InboundFileSynchronizationImpl.CONTENT_MD5;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.integration.aws.s3.core.AmazonS3Object;
import org.springframework.integration.aws.s3.core.AmazonS3Operations;
import org.springframework.integration.aws.s3.core.PaginatedObjectsView;
import org.springframework.integration.aws.s3.core.S3ObjectSummary;
import org.springframework.util.StringUtils;
/**
* The utility class for mocking the {@link AmazonS3Operations}
*
* @author Amol Nayak
*
* @since 0.5
*/
public final class AmazonS3OperationsMockingUtil {
public static final String BUCKET = "com.si.aws.test.bucket";
private static final List<S3ObjectSummary> summary = new ArrayList<S3ObjectSummary>();
private static final Map<String, String[]> objectDetails = new HashMap<String, String[]>();
private AmazonS3OperationsMockingUtil() {
throw new AssertionError("Cannot instantiate utility class");
}
public static final AmazonS3Operations mockS3Operations() {
AmazonS3Operations operations;
PaginatedObjectsView view = new PaginatedObjectsView() {
@Override
public boolean hasMoreResults() {
return false;
}
@Override
public List<S3ObjectSummary> getObjectSummary() {
return summary;
}
@Override
public String getNextMarker() {
return null;
}
};
operations = mock(AmazonS3Operations.class);
when(operations.listObjects(anyString(), anyString(), anyString(), anyInt()))
.thenReturn(view);
when(operations.getObject(anyString(), anyString(), anyString()))
.then(new Answer<AmazonS3Object>() {
public AmazonS3Object answer(InvocationOnMock invocation)
throws Throwable {
String folderName = (String)invocation.getArguments()[1];
String fileName = (String)invocation.getArguments()[2];
if(StringUtils.hasText(folderName)) {
if(folderName.startsWith("/")) {
folderName = folderName.substring(1);
}
if(folderName.endsWith("/")) {
folderName = folderName + "/";
}
}
else {
folderName = "";
}
String[] object = objectDetails.get(folderName + fileName);
AmazonS3Object s3Object;
if(object != null) {
s3Object = new AmazonS3ObjectBuilder()
.fromInputStream(new ByteArrayInputStream(object[1].getBytes()))
.withUserMetaData(Collections.singletonMap(CONTENT_MD5, object[2]))
.build();
}
else {
s3Object = null;
}
return s3Object;
}
});
return operations;
}
/**
* The private helper method that is used to mock the {@link AmazonS3Operations#listObjects(String, String, String, int)
* method
*
* The method accepts a List of object[] where each element of object array has the
* following significance
*
* object[0] is the key of the file
* object[1] is the content of the file
* object[2] is the MD5 content to be used (optional)
* object[3] is the etag of the object
*
* @param listObjects
*/
public static void mockAmazonS3Operations(final List<String[]> listObjects) {
summary.clear();
for(String[] listObject:listObjects) {
addToSummaryList(listObject);
objectDetails.put(listObject[0], listObject);
}
}
/**
* Creates a {@link S3ObjectSummary} with the given details and adds it to the summary object list
* @param listObject
*/
private static void addToSummaryList(final String[] listObject) {
summary.add(
new S3ObjectSummary() {
@Override
public long getSize() {
return 0;
}
@Override
public Date getLastModified() {
return null;
}
@Override
public String getKey() {
return listObject[0];
}
@Override
public String getETag() {
if(listObject[3] != null) {
return listObject[3];
}
else {
byte[] b64 = Base64.decodeBase64((listObject[2]).getBytes());
return Hex.encodeHexString(b64);
}
}
@Override
public String getBucketName() {
return null;
}
}
);
}
}

View File

@@ -1,115 +0,0 @@
/*
* Copyright 2002-2014 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.s3;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
/**
* The test class for {@link DefaultFileNameGenerationStrategy}
*
* @author Amol Nayak
* @author Rob Harrop
*
* @since 0.5
*
*/
public class DefaultFileNameGenerationStrategyTests {
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
/**
* Tests with the file name present in the predetermined header "file_name" of the message
*/
@Test
public void withNameInHeader() {
Message<String> message = MessageBuilder.withPayload("SomeString")
.setHeader(AmazonS3MessageHeaders.FILE_NAME, "FileName.txt")
.build();
DefaultFileNameGenerationStrategy strategy = new DefaultFileNameGenerationStrategy();
Assert.assertEquals("FileName.txt", strategy.generateFileName(message));
}
/**
* Tests with a payload as a temp file payload
* @throws IOException
*/
@Test
public void withATempFile() throws IOException {
final File file = tempFolder.newFile("TempFile.txt.writing");
Message<File> message = MessageBuilder.withPayload(file)
.build();
DefaultFileNameGenerationStrategy strategy = new DefaultFileNameGenerationStrategy();
Assert.assertEquals("TempFile.txt", strategy.generateFileName(message));
file.delete();
}
/**
* Tests with a payload as a temp file payload
* @throws IOException
*/
@Test
public void withANonTempFile() throws IOException {
final File file = tempFolder.newFile("TempFile.txt");
Message<File> message = MessageBuilder.withPayload(file)
.build();
DefaultFileNameGenerationStrategy strategy = new DefaultFileNameGenerationStrategy();
Assert.assertEquals("TempFile.txt", strategy.generateFileName(message));
file.delete();
}
/**
* Tests with a payload as a temp file payload
*/
@Test
public void withMessageIdName() {
Message<String> message = MessageBuilder.withPayload("String")
.build();
DefaultFileNameGenerationStrategy strategy = new DefaultFileNameGenerationStrategy();
UUID uid = message.getHeaders().getId();
Assert.assertEquals(uid.toString() + ".ext", strategy.generateFileName(message));
}
/**
* Tests with the file name generation expression as a null value
*/
@Test(expected=IllegalArgumentException.class)
public void withNullExprssion() {
DefaultFileNameGenerationStrategy strategy = new DefaultFileNameGenerationStrategy();
strategy.setFileNameExpression(null);
}
/**
* Tests with a null value for temporary suffix
*/
@Test(expected=IllegalArgumentException.class)
public void withNullTemporarySuffix() {
DefaultFileNameGenerationStrategy strategy = new DefaultFileNameGenerationStrategy();
strategy.setTemporarySuffix(null);
}
}

View File

@@ -1,125 +0,0 @@
/*
* 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.s3;
import org.junit.Assert;
import org.junit.Test;
/**
*
* The test cases for various {@link FileNameFilter} implementations.
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class FileNameFilterTests {
/**
* The case sets the folder of the filter to null so that it accepts all the files in the same
* folder, the {@link AbstractFileNameFilter#isAcceptSubFolders() returns false.
*
*/
@Test
public void acceptAllFilesWithoutSubfolders() {
AbstractFileNameFilter filter = new AlwaysTrueFileNamefilter();
filter.setAcceptSubFolders(false);
Assert.assertTrue(filter.accept("SomeFile.txt"));
Assert.assertFalse(filter.accept("somessubfolder/SomeFile.txt"));
}
/**
* The case sets the folder of the filter to null so that it accepts all the files in the same
* folder and the sub folders, the {@link AbstractFileNameFilter#isAcceptSubFolders() returns true.
*
*/
@Test
public void acceptAllFilesWithSubfolders() {
AbstractFileNameFilter filter = new AlwaysTrueFileNamefilter();
filter.setAcceptSubFolders(true);
Assert.assertTrue(filter.accept("SomeFile.txt"));
Assert.assertTrue(filter.accept("somessubfolder/SomeFile.txt"));
}
/**
* The test case sets a sub folder for the search and sets the
* {@link AbstractFileNameFilter#setAcceptSubFolders(boolean) to false
*/
@Test
public void acceptInSubfolderWithoutSubfolder() {
AbstractFileNameFilter filter = new AlwaysTrueFileNamefilter();
filter.setFolderName("/subfolder");
filter.setAcceptSubFolders(false);
Assert.assertFalse(filter.accept("FileName.txt"));
Assert.assertTrue(filter.accept("subfolder/FileName.txt"));
Assert.assertFalse(filter.accept("subfolder/anothersf/FileName.txt"));
}
/**
* The test case sets a sub folder for the search and sets the
* {@link AbstractFileNameFilter#setAcceptSubFolders(boolean) to true
*/
@Test
public void acceptInSubfolderWithSubfolder() {
AbstractFileNameFilter filter = new AlwaysTrueFileNamefilter();
filter.setFolderName("/subfolder");
filter.setAcceptSubFolders(true);
Assert.assertFalse(filter.accept("FileName.txt"));
Assert.assertTrue(filter.accept("subfolder/FileName.txt"));
Assert.assertTrue(filter.accept("subfolder/anothersf/FileName.txt"));
}
/**
* Tests the regex file filter
*/
@Test
public void regexTest() {
//accept only file names with name in lower case and ends with .txt
AbstractFileNameFilter filter = new RegexFileNameFilter("[a-z]+\\.txt");
filter.setAcceptSubFolders(true);
Assert.assertTrue(filter.accept("test.txt"));
Assert.assertFalse(filter.accept("Test.txt"));
Assert.assertFalse(filter.accept("test123.txt"));
Assert.assertFalse(filter.accept("test.tx"));
Assert.assertFalse(filter.accept("test"));
Assert.assertTrue(filter.accept("test/test.txt"));
Assert.assertTrue(filter.accept("test/Test/12/test.txt"));
Assert.assertFalse(filter.accept("test/Test/12/test.tx"));
Assert.assertFalse(filter.accept("test/Test/12/Test.txt"));
}
/**
* Tests the wildcard file filter
*/
@Test
public void wildCardTest() {
//accept only file names with name in lower case and ends with .txt
AbstractFileNameFilter filter = new WildcardFileNameFilter("*.txt");
filter.setAcceptSubFolders(true);
Assert.assertTrue(filter.accept("test.txt"));
Assert.assertTrue(filter.accept("Test.txt"));
Assert.assertTrue(filter.accept("test123.txt"));
Assert.assertFalse(filter.accept("test.tx"));
Assert.assertFalse(filter.accept("test"));
Assert.assertTrue(filter.accept("test/test.txt"));
Assert.assertTrue(filter.accept("test/Test/12/test.txt"));
Assert.assertTrue(filter.accept("test/Test/12/Test.txt"));
Assert.assertFalse(filter.accept("test/Test/12/Test.ext"));
}
}

View File

@@ -1,462 +0,0 @@
/*
* 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.s3;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.springframework.integration.aws.common.AWSTestUtils.assertFileContent;
import static org.springframework.integration.aws.common.AWSTestUtils.getContentsRecursively;
import static org.springframework.integration.aws.common.AWSTestUtils.md5Hash;
import static org.springframework.integration.aws.common.AWSTestUtils.writeToFile;
import static org.springframework.integration.aws.s3.AmazonS3OperationsMockingUtil.BUCKET;
import static org.springframework.integration.aws.s3.AmazonS3OperationsMockingUtil.mockAmazonS3Operations;
import static org.springframework.integration.aws.s3.AmazonS3OperationsMockingUtil.mockS3Operations;
import java.io.File;
import java.util.Arrays;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.integration.aws.s3.core.AmazonS3Operations;
import org.springframework.integration.test.util.TestUtils;
/**
* Test class for {@link InboundFileSynchronizationImpl}
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class InboundFileSynchronizationImplTests {
private static AmazonS3Operations operations;
@Rule
public final TemporaryFolder tempFolder = new TemporaryFolder();
@BeforeClass
public static void setup() {
operations = mockS3Operations();
}
private InboundFileSynchronizationImpl getInboundFileSynchronizationImpl() {
//uncomment the below code if you want to execute against the actual bucket
//but before that you need to do the following
//create a bucket and set that in the constant BUCKET in the class
//1. add a file test.txt with content test.txt to the root.
//2. create a folder sub1 and add a file test.txt to it with content sub1/test.txt.
//3. create a folder sub1/sub11 and add a file test.txt to it with content sub1/sub11/test.txt.
//4. create a folder sub2 and add a file test.txt to it with content sub/test.txt.
// DefaultAmazonS3Operations operations = new DefaultAmazonS3Operations(AmazonWSTestUtils.getCredentials());
// try {
// operations.afterPropertiesSet();
// } catch (Exception e) {
// e.printStackTrace();
// }
InboundFileSynchronizationImpl sync = new InboundFileSynchronizationImpl(operations,
new InboundLocalFileOperationsImpl());
return sync;
}
/**
* Tests with {@link AmazonS3Operations} instance as null
*/
@Test(expected=IllegalArgumentException.class)
public void withS3OperationsAsNull() {
new InboundFileSynchronizationImpl(null, new InboundLocalFileOperationsImpl());
}
/**
* Tests with {@link InboundLocalFileOperations} instance as null
*/
@Test(expected=IllegalArgumentException.class)
public void withLocalFileOperationsAsNull() {
new InboundFileSynchronizationImpl(operations, null);
}
/**
* Tests after setting both the wildcard and filename regex
*/
@Test(expected=IllegalArgumentException.class)
public void withBothWildCardAndRegex() throws Exception {
InboundFileSynchronizationImpl sync = getInboundFileSynchronizationImpl();
sync.setFileWildcard("*.txt");
sync.setFileNamePattern("[a-z]+\\.txt");
sync.afterPropertiesSet();
}
/**
* Sets non of regex and wildcard
*/
@Test
public void withNoneOfRegexAndWildcard() throws Exception {
InboundFileSynchronizationImpl impl = getInboundFileSynchronizationImpl();
impl.afterPropertiesSet();
assertEquals(AlwaysTrueFileNamefilter.class,
TestUtils.getPropertyValue(impl, "filter", FileNameFilter.class).getClass());
}
/**
* Tests after setting filename regex only
*/
@Test
public void withRegexOnly() throws Exception {
InboundFileSynchronizationImpl sync = getInboundFileSynchronizationImpl();
sync.setFileNamePattern("[a-z]+\\.txt");
sync.afterPropertiesSet();
FileNameFilter filter = TestUtils.getPropertyValue(sync, "filter",FileNameFilter.class);
assertNotNull(filter);
assertEquals(RegexFileNameFilter.class, filter.getClass());
assertEquals("[a-z]+\\.txt",
TestUtils.getPropertyValue(filter, "filter.pattern.pattern", String.class));
}
/**
* Tests after setting filename wildcard only
*/
@Test
public void withWildcardOnly() throws Exception {
InboundFileSynchronizationImpl sync = getInboundFileSynchronizationImpl();
sync.setFileWildcard("*.txt");
sync.afterPropertiesSet();
FileNameFilter filter = TestUtils.getPropertyValue(sync, "filter",FileNameFilter.class);
assertNotNull(filter);
assertEquals(WildcardFileNameFilter.class, filter.getClass());
assertEquals("*.txt",
TestUtils.getPropertyValue(filter, "filter.wildcards", String[].class)[0]);
}
/**
* Sets the {@link InboundFileSynchronizationImpl#setAcceptSubFolders(boolean) as true
*/
@Test
public void withAcceptSubfolderAsTrue() throws Exception {
InboundFileSynchronizationImpl impl = getInboundFileSynchronizationImpl();
impl.setAcceptSubFolders(true);
impl.afterPropertiesSet();
assertTrue(TestUtils.getPropertyValue(impl, "filter.acceptSubFolders",Boolean.class).booleanValue());
assertTrue(TestUtils.getPropertyValue(impl, "fileOperations.createDirectoriesIfRequired",
Boolean.class).booleanValue());
}
/**
* Invokes with remote directory as / and create directory set to true
*/
@Test
public void withRemoteAsRootAndCreateDirectoryToTrue() throws Exception {
setupMock();
String rootDirectoryPath = tempFolder.getRoot().getAbsolutePath();
String path = String.format("%s%s%s",
rootDirectoryPath,File.separator,"test.txt");
File fileOne = new File(path);
path = String.format("%s%s%s%s%s",
rootDirectoryPath,File.separator,"sub1",File.separator,"test.txt");
File fileTwo = new File(path);
path = String.format("%s%s%s%s%s%s%s",
rootDirectoryPath,File.separator,"sub1",File.separator,"sub11",File.separator,"test.txt");
File fileThree = new File(path);;
path = String.format("%s%s%s%s%s",
rootDirectoryPath,File.separator,"sub2",File.separator,"test.txt");
File fileFour = new File(path);
assertFalse(fileOne.exists());
assertFalse(fileTwo.exists());
assertFalse(fileThree.exists());
assertFalse(fileFour.exists());
InboundFileSynchronizationImpl impl = getInboundFileSynchronizationImpl();
impl.setAcceptSubFolders(true);
impl.afterPropertiesSet();
impl.synchronizeToLocalDirectory(tempFolder.getRoot(), BUCKET, "/");
assertTrue(fileOne.exists());
assertFileContent(fileOne, "test.txt");
assertTrue(fileTwo.exists());
assertFileContent(fileTwo, "sub1/test.txt");
assertTrue(fileThree.exists());
assertFileContent(fileThree, "sub1/sub11/test.txt");
assertTrue(fileFour.exists());
assertFileContent(fileFour, "sub2/test.txt");
assertEquals(4, getContentsRecursively(tempFolder.getRoot()).size());
}
/**
* Invokes with remote directory as / and create directory set to false
*/
@Test
public void withRemoteAsRootAndCreateDirectoryToFalse() throws Exception {
setupMock();
String rootDirectoryPath = tempFolder.getRoot().getAbsolutePath();
String path = String.format("%s%s%s",
rootDirectoryPath,File.separator,"test.txt");
File fileOne = new File(path);
assertFalse(fileOne.exists());
InboundFileSynchronizationImpl impl = getInboundFileSynchronizationImpl();
impl.setAcceptSubFolders(false);
impl.afterPropertiesSet();
impl.synchronizeToLocalDirectory(tempFolder.getRoot(), BUCKET, "/");
assertTrue(fileOne.exists());
assertFileContent(fileOne, "test.txt");
assertEquals(1, getContentsRecursively(tempFolder.getRoot()).size());
}
/**
* Invokes with remote directory as /sub1 and create directory set to true
*/
@Test
public void withRemoteAssub1AndCreateDirectoryToTrue() throws Exception {
setupMock();
String rootDirectoryPath = tempFolder.getRoot().getAbsolutePath();
String path = String.format("%s%s%s%s%s",
rootDirectoryPath,File.separator,"sub1",File.separator,"test.txt");
File fileOne = new File(path);
path = String.format("%s%s%s%s%s%s%s",
rootDirectoryPath,File.separator,"sub1",File.separator,"sub11",File.separator,"test.txt");
File fileTwo = new File(path);
assertFalse(fileOne.exists());
assertFalse(fileTwo.exists());
InboundFileSynchronizationImpl impl = getInboundFileSynchronizationImpl();
impl.setAcceptSubFolders(true);
impl.afterPropertiesSet();
impl.synchronizeToLocalDirectory(tempFolder.getRoot(), BUCKET, "/sub1");
assertTrue(fileOne.exists());
assertFileContent(fileOne, "sub1/test.txt");
assertTrue(fileTwo.exists());
assertFileContent(fileTwo, "sub1/sub11/test.txt");
assertEquals(2, getContentsRecursively(tempFolder.getRoot()).size());
}
/**
* Invokes with remote directory as /sub1 and create directory set to false
*/
@Test
public void withRemoteAssub1AndCreateDirectoryToFalse() throws Exception {
setupMock();
String rootDirectoryPath = tempFolder.getRoot().getAbsolutePath();
String path = String.format("%s%s%s%s%s",
rootDirectoryPath,File.separator,"sub1",File.separator,"test.txt");
File file = new File(path);
assertFalse(file.exists());
InboundFileSynchronizationImpl impl = getInboundFileSynchronizationImpl();
impl.setAcceptSubFolders(false);
impl.afterPropertiesSet();
impl.synchronizeToLocalDirectory(tempFolder.getRoot(), BUCKET, "/sub1");
assertTrue(file.exists());
assertFileContent(file, "sub1/test.txt");
assertEquals(1, getContentsRecursively(tempFolder.getRoot()).size());
}
/**
* Invokes with remote directory as / and create directory set to false
* The two files test.txt and sub1/test.txt would already be present on
* the file system. Both test.txt and sub/test.txt will have content different that the remote one.
* test.txt will be replaced and sub/test.txt will not be replaced.
*
*/
@Test
public void withRemoteAsRootAndCreateDirectoryToFalse2() throws Exception {
setupMock();
String rootDirectoryPath = tempFolder.getRoot().getAbsolutePath();
//create test.txt and sub1/test.txt
String path = String.format("%s%s%s",
rootDirectoryPath,File.separator,"test.txt");
writeToFile(path, "OldContents");
File fileOne = new File(path);
assertTrue(fileOne.exists());
assertFileContent(fileOne, "OldContents");
path = String.format("%s%s%s%s%s",
rootDirectoryPath,File.separator,"sub1",File.separator,"test.txt");
writeToFile(path, "OldContents");
File fileTwo = new File(path);
assertTrue(fileTwo.exists());
assertFileContent(fileTwo, "OldContents");
InboundFileSynchronizationImpl impl = getInboundFileSynchronizationImpl();
impl.setAcceptSubFolders(false);
impl.afterPropertiesSet();
impl.synchronizeToLocalDirectory(tempFolder.getRoot(), BUCKET, "/");
assertTrue(fileOne.exists());
assertFileContent(fileOne, "test.txt");
assertTrue(fileTwo.exists());
assertFileContent(fileTwo, "OldContents");
assertEquals(2, getContentsRecursively(tempFolder.getRoot()).size());
}
/**
* Invokes with remote directory as / and create directory set to true
* The two files test.txt and sub1/test.txt would already be present on
* the file system. Both test.txt and sub/test.txt will have content different that the remote one.
* test.txt and sub/test.txt both will be replaced.
*
*/
@Test
public void withRemoteAsRootAndCreateDirectoryToTrue2() throws Exception {
setupMock();
String rootDirectoryPath = tempFolder.getRoot().getAbsolutePath();
//create test.txt and sub1/test.txt
String path = String.format("%s%s%s",
rootDirectoryPath,File.separator,"test.txt");
writeToFile(path, "OldContents");
File fileOne = new File(path);
assertTrue(fileOne.exists());
assertFileContent(fileOne, "OldContents");
path = String.format("%s%s%s%s%s",
rootDirectoryPath,File.separator,"sub1",File.separator,"test.txt");
writeToFile(path, "OldContents");
File fileTwo = new File(path);
assertTrue(fileTwo.exists());
assertFileContent(fileTwo, "OldContents");
InboundFileSynchronizationImpl impl = getInboundFileSynchronizationImpl();
impl.setAcceptSubFolders(true);
impl.afterPropertiesSet();
impl.synchronizeToLocalDirectory(tempFolder.getRoot(), BUCKET, "/");
assertTrue(fileOne.exists());
assertFileContent(fileOne, "test.txt");
assertTrue(fileTwo.exists());
assertFileContent(fileTwo, "sub1/test.txt");
assertEquals(4, getContentsRecursively(tempFolder.getRoot()).size());
}
/**
* The case is slightly different then previous ones.
* We list from the /sub1 with /sub1/test.txt and /sub1/sub11/test.txt
* having different contents, however the etag of
* /sub1/sub11/test.txt is same as remote one hence the local one should not get replaced
* where as /sub1/test.txt should.
*
*/
@Test
public void withRemoteAsRootAndCreateDirectoryToTrue3() throws Exception {
mockAmazonS3Operations(Arrays.asList(
new String[]{"test.txt","test.txt",md5Hash("test.txt"),null},
new String[]{"sub1/test.txt","sub1/test.txt",md5Hash("sub1/test.txt"),null},
new String[]{"sub1/sub11/test.txt","sub1/sub11/test.txt",md5Hash("OldContents"),null},
new String[]{"sub2/test.txt","sub2/test.txt",md5Hash("sub2/test.txt"),null}
));
withinSub1FolderTests(true);
}
/**
* The scenario tests by listing the directory /sub1 which has two files
* /sub1/test.txt and /sub1/sub11/test.txt. The MD5 of the file will be absent and the etag is
* for MultiUpload. This should force replace the file irrespective of the content.
*/
@Test
public void withMultipartUploadForceReplace() throws Exception {
mockAmazonS3Operations(Arrays.asList(
new String[]{"sub1/test.txt","sub1/test.txt",null,
new String(Hex.encodeHex(Base64.decodeBase64(md5Hash("SomeContentSub1").getBytes()))) + "-1"},
new String[]{"sub1/sub11/test.txt","sub1/sub11/test.txt",null,
new String(Hex.encodeHex(Base64.decodeBase64(md5Hash("SomeContentSub1/Sub11").getBytes()))) + "-1"}
));
withinSub1FolderTests(false);
}
/**
* The scenario will test with two files present in /sub1 directory, /sub1/test.txt and
* /sub1/sub11/test.txt. Now both these files have multipart upload etag but both have
* MD5 hash in the user's metadata. The contents of both the files is different than the one
* on remote but /sub/test.txt has MD5 sum same as remote, so this should not get replaced
*
* @throws Exception
*/
@Test
public void withMultipartUploadWithMD5Metadata() throws Exception {
mockAmazonS3Operations(Arrays.asList(
new String[]{"sub1/test.txt","sub1/test.txt",md5Hash("sub1/test.txt"),
new String(Hex.encodeHex(Base64.decodeBase64(md5Hash("sub1/test.txt").getBytes()))) + "-1"},
new String[]{"sub1/sub11/test.txt","sub1/sub11/test.txt",md5Hash("OldContents"),
new String(Hex.encodeHex(Base64.decodeBase64(md5Hash("OldContents").getBytes()))) + "-1"}
));
withinSub1FolderTests(true);
}
/**
* Private method that extracts the common assertion logic for the files in sub1 folder
* @throws Exception
*/
private void withinSub1FolderTests(boolean acceptSubfolder) throws Exception {
String rootDirectoryPath = tempFolder.getRoot().getAbsolutePath();
//create sub1/sub11/test.txt and sub1/test.txt
String path = String.format("%s%s%s%s%s%s%s",
rootDirectoryPath,File.separator,"sub1",File.separator,"sub11",File.separator,"test.txt");
writeToFile(path, "OldContents");
File fileOne = new File(path);
assertTrue(fileOne.exists());
assertFileContent(fileOne, "OldContents");
path = String.format("%s%s%s%s%s",
rootDirectoryPath,File.separator,"sub1",File.separator,"test.txt");
writeToFile(path, "OldContents");
File fileTwo = new File(path);
assertTrue(fileTwo.exists());
assertFileContent(fileTwo, "OldContents");
InboundFileSynchronizationImpl impl = getInboundFileSynchronizationImpl();
impl.setAcceptSubFolders(acceptSubfolder);
impl.afterPropertiesSet();
impl.synchronizeToLocalDirectory(tempFolder.getRoot(), BUCKET, "/sub1");
assertTrue(fileOne.exists());
assertFileContent(fileOne, "OldContents");
assertTrue(fileTwo.exists());
assertFileContent(fileTwo, "sub1/test.txt");
assertEquals(2, getContentsRecursively(tempFolder.getRoot()).size());
}
/**
* Private helper method that will be setup mock s3 operations to give an illusion
* that it has 4 objects in the remote bucket
*
*/
private void setupMock() {
mockAmazonS3Operations(Arrays.asList(
new String[]{"test.txt","test.txt",md5Hash("test.txt"),null},
new String[]{"sub1/test.txt","sub1/test.txt",md5Hash("sub1/test.txt"),null},
new String[]{"sub1/sub11/test.txt","sub1/sub11/test.txt",md5Hash("sub1/sub11/test.txt"),null},
new String[]{"sub2/test.txt","sub2/test.txt",md5Hash("sub2/test.txt"),null}
));
}
}

View File

@@ -1,177 +0,0 @@
/*
* 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.s3;
import static org.springframework.integration.aws.common.AWSTestUtils.assertFileContent;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Collections;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.integration.test.util.TestUtils;
/**
* The test class for {@link InboundLocalFileOperationsImpl} that is used to perform
* operations on local file system
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class InboundLocalFileOperationsImplTests {
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
/**
* Tries registering a null listener with the class
*/
@Test(expected=IllegalArgumentException.class)
public void withNullListener() {
InboundLocalFileOperations operations = new InboundLocalFileOperationsImpl();
operations.addEventListener(null);
}
/**
* Tries setting the listeners which is an empty list
*/
@SuppressWarnings("unchecked")
@Test(expected=IllegalArgumentException.class)
public void setEmptyListeners() {
InboundLocalFileOperations operations = new InboundLocalFileOperationsImpl();
operations.setEventListeners(Collections.EMPTY_LIST);
}
/**
* Test case for setting a temporary suffix that begins with a .
*/
@Test
public void setTempSuffixBeginningWithDot() {
InboundLocalFileOperations operations = new InboundLocalFileOperationsImpl();
operations.setTemporaryFileSuffix(".write");
Assert.assertEquals(".write", TestUtils.getPropertyValue(operations, "tempFileSuffix"));
}
/**
* Test case for setting a temporary suffix that does not begins with a .
*/
@Test
public void setTempSuffixNotBeginningWithDot() {
InboundLocalFileOperations operations = new InboundLocalFileOperationsImpl();
operations.setTemporaryFileSuffix("write");
Assert.assertEquals(".write", TestUtils.getPropertyValue(operations, "tempFileSuffix"));
}
/**
*Since the provided directory is null, we expect an {@link IllegalArgumentException}
*/
@Test(expected=IllegalArgumentException.class)
public void writeWithNullDirectory() throws Exception {
InboundLocalFileOperations operations = new InboundLocalFileOperationsImpl();
operations.writeToFile(null, null, null);
}
/**
*Since the provided file name is null, we expect an {@link IllegalArgumentException}
*/
@Test(expected=IllegalArgumentException.class)
public void writeWithNullFileName() throws Exception {
InboundLocalFileOperations operations = new InboundLocalFileOperationsImpl();
operations.writeToFile(tempFolder.newFolder("Test"), null, null);
}
/**
*Since the provided stream as null, we expect an {@link IllegalArgumentException}
*/
@Test(expected=IllegalArgumentException.class)
public void writeWithNullStream() throws Exception {
InboundLocalFileOperations operations = new InboundLocalFileOperationsImpl();
operations.writeToFile(tempFolder.newFolder("Test"), "TestFile.txt", null);
}
/**
*Provided {@link File} for directory exists and is not a directory
*/
@Test(expected=IllegalArgumentException.class)
public void writeWithExistantNonDirectory() throws Exception {
InboundLocalFileOperations operations = new InboundLocalFileOperationsImpl();
operations.writeToFile(tempFolder.newFile("Test"), "TestFile.txt", new ByteArrayInputStream(new byte[]{}));
}
/**
*Provided {@link File} for directory does not exist exists and the create flag is false
*/
@Test(expected=IllegalArgumentException.class)
public void writeWithNonExistentDirectory() throws Exception {
InboundLocalFileOperations operations = new InboundLocalFileOperationsImpl();
operations.writeToFile(new File(tempFolder.getRoot() + "SomeDir"), "TestFile.txt", new ByteArrayInputStream(new byte[]{}));
}
/**
* Writes some test content to the file
*/
@Test
public void writeTestContentToFile() throws Exception {
InboundLocalFileOperations operations = new InboundLocalFileOperationsImpl();
operations.setCreateDirectoriesIfRequired(true);
File directory = new File(tempFolder.getRoot() + File.separator + "someNestedDir");
File tempFile = new File(directory.getAbsolutePath() + File.separator + "SomeFileName.txt.writing");
File permFile = new File(directory.getAbsolutePath() + File.separator + "SomeFileName.txt");
Assert.assertFalse(tempFile.exists());
Assert.assertFalse(permFile.exists());
operations.writeToFile(directory, "SomeFileName.txt", new ByteArrayInputStream("Some Test Content".getBytes()));
Assert.assertFalse(tempFile.exists());
Assert.assertTrue(permFile.exists());
//Check the content
assertFileContent(permFile, "Some Test Content");
//TODO: Test FileEventHandlers
}
/**
* Writes some test content to the file with teh given target file existent
*/
@Test
public void writeTestContentWithTargetExistent() throws Exception {
InboundLocalFileOperations operations = new InboundLocalFileOperationsImpl();
operations.setCreateDirectoriesIfRequired(true);
File directory = tempFolder.newFolder("someNestedDir");
File tempFile = new File(directory.getAbsolutePath() + File.separator + "SomeFileName.txt.writing");
File permFile = new File(directory.getAbsolutePath() + File.separator + "SomeFileName.txt");
permFile.createNewFile();
//Write Some content
FileOutputStream fos = new FileOutputStream(permFile);
fos.write("Some Old Contents".getBytes());
fos.close();
assertFileContent(permFile, "Some Old Contents");
Assert.assertFalse(tempFile.exists());
Assert.assertTrue(permFile.exists());
operations.writeToFile(directory, "SomeFileName.txt", new ByteArrayInputStream("Some Test Content".getBytes()));
Assert.assertFalse(tempFile.exists());
Assert.assertTrue(permFile.exists());
//Check the content
assertFileContent(permFile, "Some Test Content");
}
}

View File

@@ -1,55 +0,0 @@
/*
* 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.s3.config.xml;
import org.springframework.integration.aws.s3.core.AmazonS3Object;
import org.springframework.integration.aws.s3.core.AmazonS3Operations;
import org.springframework.integration.aws.s3.core.PaginatedObjectsView;
/**
* The dummy {@link AmazonS3Operations} for tests
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class AmazonS3DummyOperations implements AmazonS3Operations {
@Override
public PaginatedObjectsView listObjects(String bucketName,
String folder, String nextMarker, int pageSize) {
return null;
}
@Override
public void putObject(String bucketName, String folder,
String objectName, AmazonS3Object s3Object) {
}
@Override
public AmazonS3Object getObject(String bucketName, String folder,
String objectName) {
return null;
}
@Override
public boolean removeObject(String bucketName, String folder,
String objectName) {
return false;
}
}

View File

@@ -1,85 +0,0 @@
/*
* 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.s3.config.xml;
import static org.junit.Assert.assertEquals;
import static org.springframework.integration.test.util.TestUtils.getPropertyValue;
import java.io.File;
import java.net.URI;
import org.junit.Test;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.aws.s3.AmazonS3InboundSynchronizationMessageSource;
import org.springframework.integration.aws.s3.core.AmazonS3Operations;
import org.springframework.integration.aws.s3.core.DefaultAmazonS3Operations;
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
/**
* The test case class for S3 inbound channel adapter
*
* @author Amol Nayak
*
* @since 0.5
*
*/
public class AmazonS3InboundChannelAdapterParserTests {
/**
* Tests the inbound channel adapter definition with a valid combination of attributes
*/
@Test
public void withValidAttributeValues() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:s3-valid-inbound-cases.xml");
SourcePollingChannelAdapter valid = ctx.getBean("validInbound", SourcePollingChannelAdapter.class);
AmazonS3InboundSynchronizationMessageSource source = getPropertyValue(valid, "source", AmazonS3InboundSynchronizationMessageSource.class);
assertEquals("TestBucket", getPropertyValue(source, "bucket"));
assertEquals(".temp", getPropertyValue(source, "temporarySuffix"));
assertEquals(new File(System.getProperty("java.io.tmpdir")), getPropertyValue(source, "directory"));
assertEquals("remote", getPropertyValue(source, "remoteDirectory"));
assertEquals(true, getPropertyValue(source, "acceptSubFolders", Boolean.class).booleanValue());
assertEquals(100, getPropertyValue(source, "maxObjectsPerBatch", Integer.class).intValue());
assertEquals("[A-Za-z0-9]+\\\\.txt", getPropertyValue(source, "fileNameRegex"));
//test the second definition with custom attributes
valid = ctx.getBean("validInboundWithCustomOps", SourcePollingChannelAdapter.class);
source = getPropertyValue(valid, "source", AmazonS3InboundSynchronizationMessageSource.class);
AmazonS3Operations s3Operations = getPropertyValue(source, "s3Operations", AmazonS3Operations.class);
assertEquals(AmazonS3DummyOperations.class, s3Operations.getClass());
//test with aws endpoint set
valid = ctx.getBean("withAWSEndpoint", SourcePollingChannelAdapter.class);
source = getPropertyValue(valid, "source", AmazonS3InboundSynchronizationMessageSource.class);
s3Operations = getPropertyValue(source, "s3Operations", AmazonS3Operations.class);
assertEquals(DefaultAmazonS3Operations.class, s3Operations.getClass());
assertEquals("https://s3-eu-west-1.amazonaws.com", getPropertyValue(s3Operations, "client.endpoint", URI.class).toString());
ctx.close();
}
/**
* Tests with a definition where none of directory and directory-expression attributes are provided
*/
@Test(expected=BeanDefinitionStoreException.class)
public void withNoneOfDirectoryExprAndDirectory() {
new ClassPathXmlApplicationContext("classpath:s3-with-none-of-direxpr-and-dir.xml");
}
}

View File

@@ -1,158 +0,0 @@
/*
* Copyright 2002-2014 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.s3.config.xml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.springframework.integration.test.util.TestUtils.getPropertyValue;
import java.net.URI;
import org.junit.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.integration.aws.s3.AmazonS3MessageHandler;
import org.springframework.integration.aws.s3.FileNameGenerationStrategy;
import org.springframework.integration.aws.s3.core.AmazonS3Operations;
import org.springframework.integration.aws.s3.core.DefaultAmazonS3Operations;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.messaging.Message;
/**
* The test case for the aws-s3 namespace's {@link AmazonS3OutboundChannelAdapterParser} class
*
* @author Amol Nayak
* @author Rob Harrop
*
* @since 0.5
*
*/
public class AmazonS3OutboundChannelAdapterParserTests {
/**
* Test case for the xml definition with a custom implementation of {@link AmazonS3Operations}
*
*/
@Test
public void withCustomOperations() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:s3-valid-outbound-cases.xml");
EventDrivenConsumer consumer = ctx.getBean("withCustomService",EventDrivenConsumer.class);
AmazonS3MessageHandler handler = getPropertyValue(consumer, "handler", AmazonS3MessageHandler.class);
assertEquals(AmazonS3DummyOperations.class, getPropertyValue(handler, "operations").getClass());
Expression expression =
getPropertyValue(handler, "remoteDirectoryProcessor.expression",Expression.class);
assertNotNull(expression);
assertEquals(LiteralExpression.class, expression.getClass());
assertEquals("/", getPropertyValue(expression, "literalValue", String.class));
ctx.destroy();
}
/**
* Test case for the xml definition with the default implementation of {@link AmazonS3Operations}
*/
@Test
public void withDefaultOperationsImplementation() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:s3-valid-outbound-cases.xml");
EventDrivenConsumer consumer = ctx.getBean("withDefaultServices",EventDrivenConsumer.class);
AmazonS3MessageHandler handler = getPropertyValue(consumer, "handler", AmazonS3MessageHandler.class);
assertEquals(DefaultAmazonS3Operations.class, getPropertyValue(handler, "operations").getClass());
Expression expression =
getPropertyValue(handler, "remoteDirectoryProcessor.expression",Expression.class);
assertNotNull(expression);
assertEquals(SpelExpression.class, expression.getClass());
assertEquals("headers['remoteDirectory']", getPropertyValue(expression, "expression", String.class));
assertEquals("TestBucket", getPropertyValue(handler, "bucket", String.class));
assertEquals("US-ASCII", getPropertyValue(handler, "charset", String.class));
assertEquals("dummy", getPropertyValue(handler, "credentials.accessKey", String.class));
assertEquals("dummy", getPropertyValue(handler, "credentials.secretKey", String.class));
assertEquals("dummy", getPropertyValue(handler, "operations.credentials.accessKey", String.class));
assertEquals("dummy", getPropertyValue(handler, "operations.credentials.secretKey", String.class));
assertEquals(5120, getPropertyValue(handler, "operations.multipartUploadThreshold", Long.class).longValue());
assertEquals(".write", getPropertyValue(handler, "operations.temporaryFileSuffix", String.class));
assertEquals(".write", getPropertyValue(handler, "fileNameGenerator.temporarySuffix", String.class));
assertEquals("headers['name']", getPropertyValue(handler, "fileNameGenerator.fileNameExpression", String.class));
assertEquals(ctx.getBean("executor"), getPropertyValue(handler, "operations.threadPoolExecutor"));
ctx.destroy();
}
/**
* Test case for the xml definition with a custom implementation of {@link FileNameGenerationStrategy}
*
*/
@Test
public void withCustomNameGenerator() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("s3-valid-outbound-cases.xml");
EventDrivenConsumer consumer = ctx.getBean("withCustomNameGenerator",EventDrivenConsumer.class);
AmazonS3MessageHandler handler = getPropertyValue(consumer, "handler", AmazonS3MessageHandler.class);
assertEquals(DummyFileNameGenerator.class, getPropertyValue(handler, "fileNameGenerator").getClass());
ctx.destroy();
}
/**
* Test case for the xml definition with a custom AWS endpoint
*
*/
@Test
public void withCustomEndpoint() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("s3-valid-outbound-cases.xml");
EventDrivenConsumer consumer = ctx.getBean("withCustomEndpoint",EventDrivenConsumer.class);
AmazonS3MessageHandler handler = getPropertyValue(consumer, "handler", AmazonS3MessageHandler.class);
assertEquals("http://s3-eu-west-1.amazonaws.com",
getPropertyValue(handler, "operations.client.endpoint", URI.class).toString());
ctx.destroy();
}
/**
* Multi part upload should have a size of 5120 and above, any value less than 5120 will
* thrown an exception
*/
@Test(expected=BeanCreationException.class)
public void withMultiUploadLessthan5120() {
new ClassPathXmlApplicationContext("s3-multiupload-lessthan-5120.xml");
}
/**
* Test with both the custom file generator and expression attribute set.
*/
@Test(expected=BeanDefinitionStoreException.class)
public void withBothFileGeneratorAndExpression() {
new ClassPathXmlApplicationContext("s3-both-customfilegenerator-and-expression.xml");
}
/**
* When custom implementation of {@link AmazonS3Operations} is provided, the attributes
* multipart-upload-threshold, temporary-directory, temporary-suffix and thread-pool-executor
* are not allowed
*/
@Test(expected=BeanDefinitionStoreException.class)
public void withCustomOperationsAndDisallowedAttributes() {
new ClassPathXmlApplicationContext("s3-custom-operations-with-disallowed-attributes.xml");
}
public static class DummyFileNameGenerator implements FileNameGenerationStrategy {
@Override
public String generateFileName(Message<?> message) {
return null;
}
}
}

View File

@@ -1,629 +0,0 @@
/*
* 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.s3.core;
import static org.springframework.integration.aws.common.AWSTestUtils.getCredentials;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.integration.aws.core.AWSCredentials;
import org.springframework.integration.aws.core.PropertiesAWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.AccessControlList;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.Grant;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.S3Object;
/**
* The abstract test class for testing all the common functionality for AWS operations
* on S3 using the appropriate implementation provided by the subclass.
*
* Note: To run the test, you will have to create one bucket for yourself and
* set the name in the {@link #BUCKET_NAME}
*
* @author Amol Nayak
*
* @since 0.5
*
*/
@Ignore()
public abstract class AbstractAmazonS3OperationsImplAWSTests {
private static final String VALID_CANONICAL_ID = "f854da004ee08cf4f8664334d288561c8512c508db9785388de7319ded85f8f3";
@Rule
public final TemporaryFolder temp = new TemporaryFolder();
//private static final String UPLOAD_SOURCE_DIRECTORY = System.getProperty("java.io.tmpdir") + "upload";
//To run the test, you will have to create one one bucket for yourself and
//set the name here
protected static final String BUCKET_NAME = "com.si.aws.test.bucket";
private static AmazonS3Client client;
private static PropertiesAWSCredentials credentials;
@BeforeClass
public static final void setup() throws Exception {
AWSCredentials credentials = getCredentials();
client = new AmazonS3Client(
new BasicAWSCredentials(credentials.getAccessKey(), credentials.getSecretKey()));
}
/**
* Sets the multipart threshold value of the upload to 5K,
* this should get executed successfully
*/
@Test
public void withMultipartThresholdWith5k() {
AbstractAmazonS3Operations impl = getS3OperationsImplementation();
impl.setMultipartUploadThreshold(5120);
Assert.assertEquals(5120,impl.getMultipartUploadThreshold());
}
/**
* Sets the multipart threshold value of the upload to a value < 5K,
* should throw IllegalArgumentException
*/
@Test(expected=IllegalArgumentException.class)
public void withMultipartThresholdWithLt5k() {
getS3OperationsImplementation().setMultipartUploadThreshold(5000);
}
/**
* Sets the directory path as a null value.
*/
@Test(expected=IllegalArgumentException.class)
public void setNullDirectoryString() {
getS3OperationsImplementation().setTemporaryDirectory((String)null);
}
/**
* Pass a non null string that exists
*/
@Test
public void setNonNullDirectoryString() {
//this will exist
String directory = System.getProperty("java.io.tmpdir");
getS3OperationsImplementation().setTemporaryDirectory(directory);
}
/**
* Pass a String to a directory that doesn't exist
*/
@Test(expected=IllegalArgumentException.class)
public void setNonExistentDirectory() {
//getting the current time in millis and hope no folder with that name exists
long current = System.currentTimeMillis();
DefaultAmazonS3Operations s3Service = new DefaultAmazonS3Operations(credentials);
s3Service.setTemporaryDirectory("./" + current);
}
/**
* Sets the temporary file suffix to the null value
*/
@Test(expected=IllegalArgumentException.class)
public void setNullTemporaryFileSuffix() {
getS3OperationsImplementation().setTemporaryFileSuffix(null);
}
/**
* Sets the temporary file suffix to a string that begins with a "."
*/
@Test
public void setValidTempFileSuffixStartingWithDot() {
AbstractAmazonS3Operations impl = getS3OperationsImplementation();
impl.setTemporaryFileSuffix(".tempsuff");
Assert.assertEquals(".tempsuff", impl.getTemporaryFileSuffix());
}
/**
* Sets the temporary file suffix to a string that does not begin with a "."
*/
@Test
public void setValidTempFileSuffixStartingWithoutDot() {
AbstractAmazonS3Operations impl = getS3OperationsImplementation();
impl.setTemporaryFileSuffix("tmpsuff");
Assert.assertEquals(".tmpsuff", impl.getTemporaryFileSuffix());
}
//TODO. Test all the conditions that test the folder generation logic, null folder
//null bucket etc
/**
* The AWS Service test put a file with null bucket given
*
*/
public void putToNullBucket() {
AbstractAmazonS3Operations impl = getS3OperationsImplementation();
impl.putObject(null, "/", "name", null);
}
//TODO: Execute the following cases for putObject
/**
* Executes the put object with a null bucket name, should throw
* an {@link IllegalArgumentException}
*/
@Test(expected=IllegalArgumentException.class)
public void putWithNullBucket() {
AmazonS3Operations impl = getS3OperationsImplementation();
impl.putObject(null, null, "SomeObject", null);
}
/**
* Executes the put object with a null object name, should throw
* an {@link IllegalArgumentException}
*/
@Test(expected=IllegalArgumentException.class)
public void withNullObjectName() {
AmazonS3Operations impl = getS3OperationsImplementation();
impl.putObject(BUCKET_NAME, "/", null, null);
}
/**
* Executes the put object with a null s3 object, should throw
* an {@link IllegalArgumentException}
*/
@Test(expected=IllegalArgumentException.class)
public void withNullS3Object() {
AmazonS3Operations impl = getS3OperationsImplementation();
impl.putObject(BUCKET_NAME, "/", "TestObjectName.txt", null);
}
/**
* Executes the put object with both file source and input stream provided
*
* an {@link IllegalArgumentException}
*/
@Test(expected=IllegalArgumentException.class)
public void withBothFileSourceAndInputStream() throws Exception {
String folder = temp.getRoot().getAbsolutePath();
File file = new File(folder + File.separator + "SomeTestFile.txt");
file.createNewFile();
FileInputStream fin = new FileInputStream(file);
new AmazonS3Object(null, null,fin, file);
fin.close();
file.delete();
}
/**
* Executes the put object with none of file source and input stream provided
*
* an {@link IllegalArgumentException}
*/
@Test(expected=IllegalArgumentException.class)
public void withNoneOfFileSourceAndInputStream() throws Exception {
new AmazonS3Object(null, null, null, null);
}
/**
* With a temp file, upload to the bucket and see temp file gets deleted and the object
* successfully uploaded. Also the ACL of the provided object is null, so the
* object should get default ACLs
*
*/
@Test
public void putFromTempFile() throws Exception {
//first delete the file from the AWS bucket
String key = "TestPutFromTempFile.txt";
deleteObject(BUCKET_NAME, key);
File file = generateUploadFile();
//Now put the object
AbstractAmazonS3Operations operations = getS3OperationsImplementation();
operations.setTemporaryDirectory(temp.getRoot());
FileInputStream fin = new FileInputStream(file);
//No ACL associated
AmazonS3Object object = new AmazonS3Object(null, null,fin, null);
operations.putObject(BUCKET_NAME, null, key, object);
assertTempFileDeletion(temp.getRoot().getAbsolutePath(), key);
assertObjectExistenceInBucket(key);
fin.close();
//delete the source file.
file.delete();
}
/**
* Upload a file to a folder in the given bucket with the folder name
* ending with a slash. Also the ACL of the provided object is null, so the
* object should get default ACLs.
*
*/
@Test
public void putToFolderWithEndingSlash() throws Exception {
String uploadFileName = "TestPutWithEndingSlash.txt";
String key = "somedir/with/endingslash/" + uploadFileName;
//first delete the file from the AWS bucket
deleteObject(BUCKET_NAME, key);
File file = generateUploadFile();
//Now put the object
AmazonS3Operations operations = getS3OperationsImplementation();
//No ACL associated
AmazonS3Object object = new AmazonS3Object(null, null, null, file);
operations.putObject(BUCKET_NAME, "somedir/with/endingslash/", uploadFileName, object);
assertObjectExistenceInBucket(key);
//delete the source file.
file.delete();
}
/**
* Upload a file to a folder in the given bucket with the folder name
* ending without a slash. Also the ACL of the provided object is null, so the
* object should get default ACLs.
*
*/
@Test
public void putToFolderWithoutEndingSlash() throws Exception {
String uploadFile = "TestPutWithoutEndingSlash.txt";
String key = "somedir/without/endingslash/" + uploadFile;
//first delete the file from the AWS bucket
deleteObject(BUCKET_NAME, key);
File file = generateUploadFile();
//Now put the object
AmazonS3Operations operations = getS3OperationsImplementation();
//No ACL associated
AmazonS3Object object = new AmazonS3Object(null, null, null, file);
operations.putObject(BUCKET_NAME, "somedir/without/endingslash", uploadFile, object);
assertObjectExistenceInBucket(key);
//delete the source file.
file.delete();
}
/**
* Upload a file to a folder in the given bucket with the folder name
* beginning a slash. Also the ACL of the provided object is null, so the
* object should get default ACLs.
*
*/
@Test
public void putToFolderBeginningWithSlash() throws Exception {
String uploadFileName = "TestPutBeginningWithSlash.txt";
String key = "beginning/with/slash/" + uploadFileName;
//first delete the file from the AWS bucket
deleteObject(BUCKET_NAME, key);
File file = generateUploadFile();
//Now put the object
AmazonS3Operations operations = getS3OperationsImplementation();
//No ACL associated
AmazonS3Object object = new AmazonS3Object(null, null, null, file);
operations.putObject(BUCKET_NAME, "/beginning/with/slash/", uploadFileName, object);
assertObjectExistenceInBucket(key);
//delete the source file.
file.delete();
}
/**
* Upload a file with the provided ACLs and meta data, the test verifies if the
* ACLs and the metadata of the file is appropriately set
*
*/
@Test
public void putToFolderForACLAndMetadataTest() throws Exception {
String uploadFileName = "TestObjectACLAndMetaData.txt";
String key = "acl/and/metadata/test/" + uploadFileName;
//first delete the file from the AWS bucket
deleteObject(BUCKET_NAME, key);
File file = generateUploadFile();
FileInputStream fin = new FileInputStream(file);
//Now put the object
AmazonS3Operations operations = getS3OperationsImplementation();
Map<String, String> userMetaData = Collections.singletonMap("TestKey", "TestValue");
AmazonS3ObjectACL acl = new AmazonS3ObjectACL();
ObjectGrant grant = new ObjectGrant(new Grantee(VALID_CANONICAL_ID, GranteeType.CANONICAL_GRANTEE_TYPE),
ObjectPermissions.READ_ACP);
acl.addGrant(grant);
AmazonS3Object object = new AmazonS3Object(userMetaData, null, fin, null,acl);
operations.putObject(BUCKET_NAME, "/acl/and/metadata/test/", uploadFileName, object);
//This fails somehow on my machine
//assertTempFileDeletion(uploadFileName);
//NOTE: The case of the key is no longer in the case we used, its all lower case.
//lets get the object's User metadata first
S3Object s3Object = getObject(BUCKET_NAME,key);
ObjectMetadata objectMetadata = s3Object.getObjectMetadata();
userMetaData = objectMetadata.getUserMetadata();
Assert.assertNotNull("User metadata is not expected to be null, but got null", userMetaData);
Assert.assertTrue("Expecting the key 'testkey' in user MetaData", userMetaData.containsKey("testkey"));
Assert.assertEquals("TestValue", userMetaData.get("testkey"));
//lets verify the object's ACL
AccessControlList acls = getObjectACL(BUCKET_NAME, key);
Set<Grant> grants = acls.getGrants();
boolean isACLValid = false;
for(Grant g:grants) {
com.amazonaws.services.s3.model.Grantee grantee = g.getGrantee();
if(VALID_CANONICAL_ID.equals(grantee.getIdentifier())
&& "READ_ACP".equals(grant.getPermission().toString())) {
isACLValid = true;
}
}
Assert.assertTrue("Expected Object ACl not found", isACLValid);
fin.close();
//delete the source file.
file.delete();
}
/**
* List the contents in the bucket with null bucket name
*/
@Test(expected=IllegalArgumentException.class)
public void listWithNullBucket() {
AmazonS3Operations impl = getS3OperationsImplementation();
impl.listObjects(null, "folder", null, 1);
}
/**
* List objects with negative page size
*/
@Test(expected=IllegalArgumentException.class)
public void listWithPageSizeLt0() {
AmazonS3Operations impl = getS3OperationsImplementation();
impl.listObjects(BUCKET_NAME, "folder", null, -2);
}
/**
* List with null folder
*/
@Test
public void listWithNullFolder() {
AmazonS3Operations impl = getS3OperationsImplementation();
PaginatedObjectsView pov = impl.listObjects(BUCKET_NAME, null, null, 100);
List<S3ObjectSummary> summary = pov.getObjectSummary();
Assert.assertNotNull(summary);
Assert.assertTrue(summary.size() > 0);
System.out.println("Summary list size is " + summary.size());
}
/**
* List with folder as a slash(/), for root folder
*/
@Test
public void listWithSlashOnRoot() {
AmazonS3Operations impl = getS3OperationsImplementation();
PaginatedObjectsView pov = impl.listObjects(BUCKET_NAME, "/", null, 100);
List<S3ObjectSummary> summary = pov.getObjectSummary();
Assert.assertNotNull(summary);
Assert.assertTrue(summary.size() > 0);
System.out.println("Summary list size is " + summary.size());
}
/**
* List with folder as a slash(/)
*/
@Test
public void listWithFolderAsSlash() {
AmazonS3Operations impl = getS3OperationsImplementation();
PaginatedObjectsView pov = impl.listObjects(BUCKET_NAME, "/acl", null, 100);
List<S3ObjectSummary> summary = pov.getObjectSummary();
Assert.assertNotNull(summary);
Assert.assertTrue(summary.size() > 0);
System.out.println("Summary list size is " + summary.size());
}
/**
* List with folder as a not beginning with slash(/)
*/
@Test
public void listWithFolderNotBeginningWithSlash() {
AmazonS3Operations impl = getS3OperationsImplementation();
PaginatedObjectsView pov = impl.listObjects(BUCKET_NAME, "somedir/with", null, 100);
List<S3ObjectSummary> summary = pov.getObjectSummary();
Assert.assertNotNull(summary);
Assert.assertTrue(summary.size() > 0);
System.out.println("Summary list size is " + summary.size());
}
/**
* The test case assumes that all previous AWS tests are executed and we have at least 4 objects
* in the bucket, on running all the above tests you will have 5, so we need not do anything
* special to add more objects to execute this test
*
*/
@Test
public void paginateRecords() {
AmazonS3Operations impl = getS3OperationsImplementation();
PaginatedObjectsView pov = impl.listObjects(BUCKET_NAME, "/", null, 3);
List<S3ObjectSummary> summary = pov.getObjectSummary();
Assert.assertNotNull(summary);
Assert.assertEquals(3, summary.size());
String nextMarker = pov.getNextMarker();
Assert.assertNotNull("Expected a non null marker", nextMarker);
pov = impl.listObjects(BUCKET_NAME, "/", nextMarker, 3);
summary = pov.getObjectSummary();
Assert.assertNotNull(summary);
Assert.assertTrue(summary.size() > 0);
System.out.printf("Number of records on second page are %d\n",summary.size());
}
/**
* Tests the get object with a null bucket
*/
@Test(expected=IllegalArgumentException.class)
public void getObjectFromNullBucket() {
AmazonS3Operations impl = getS3OperationsImplementation();
impl.getObject(null, null, "ObjectName.txt");
}
/**
* Gets a non existent object from the bucket, should return null on unsuccessful search
* and if the object with the key doesn't exist.
*/
@Test
public void getNonExistentObject() {
AmazonS3Operations impl = getS3OperationsImplementation();
AmazonS3Object object = impl.getObject(BUCKET_NAME, null, "jhgkmjbhdc.thb");
Assert.assertNull("Expecting a null object but got a non null one", object);
}
/**
* Invoked the getObject with null folder, this will get the object
* from the root of the bucket.
*
*/
@Test
public void getObjectWithNullFolder() {
AmazonS3Operations impl = getS3OperationsImplementation();
AmazonS3Object object = impl.getObject(BUCKET_NAME, null, "TestPutFromTempFile.txt");
Assert.assertNotNull("Expecting a non null object but got a null one", object);
}
/**
* Invoked the getObject with folder name beginning with /
*
*/
@Test
public void getObjectromFolderBeginningWithSlash() {
AmazonS3Operations impl = getS3OperationsImplementation();
AmazonS3Object object = impl.getObject(BUCKET_NAME, "/acl/and/metadata/test", "TestObjectACLAndMetaData.txt");
Assert.assertNotNull("Expecting a non null object but got a null one", object);
}
/**
* Invoked the getObject with folder name beginning with /
*
*/
@Test
public void getObjectFromFolderBeginningWithoutSlash() {
AmazonS3Operations impl = getS3OperationsImplementation();
AmazonS3Object object = impl.getObject(BUCKET_NAME, "acl/and/metadata/test", "TestObjectACLAndMetaData.txt");
Assert.assertNotNull("Expecting a non null object but got a null one", object);
}
/**
* The common method that checks if the temp file generated for the
* test file uploaded is deleted.
*/
private void assertTempFileDeletion(String rootFolder, String baseFileName) {
//Check if the temp file exists.
File tempFile = new File(rootFolder + File.separator + baseFileName + ".writing");
Assert.assertFalse("Was expecting the temp file to be deleted, but is present", tempFile.exists());
}
/**
* Common method that will assert the existence of the object with the given key in the
* bucket
*
* @param key
*/
private void assertObjectExistenceInBucket(String key) {
S3Object s3Object = getObject(BUCKET_NAME, key);
//This is not needed as an exception will be thrown if the key does not exist
Assert.assertNotNull("Non null S3Object expected",s3Object);
}
/**
* The private helper method that generates the test file to be uploaded
* @throws IOException
* @throws FileNotFoundException
*/
private File generateUploadFile() throws IOException, FileNotFoundException {
//TODO: Move this to @BeforeClass?
String fileName = System.currentTimeMillis() + ".txt";
File file = new File(temp.getRoot().getAbsolutePath() + File.separator + fileName);
file.createNewFile();
//Write something to it
FileOutputStream fos = new FileOutputStream(file);
fos.write("Test".getBytes());
fos.close();
return file;
}
//-- helper methods to interact with AWS S3 services
//These methods are used to verify and assert if the implementation
//has performed the desired operation
/**
* Gets the object from the S3 bucket
*
* @param gets the object from the bucket with the given key
*/
protected S3Object getObject(String bucket,String key) {
return client.getObject(new GetObjectRequest(bucket, key));
}
/**
* Gets the objects's {@link AccessControlList} (ACL) for the given bucket and key
*
* @param bucket
* @param key
*/
protected AccessControlList getObjectACL(String bucket,String key) {
return client.getObjectAcl(bucket, key);
}
/**
* Deleted the given object name from the given bucket and key
*
* @param bucket
* @param key
*
*/
protected void deleteObject(String bucket,String key) {
client.deleteObject(bucket, key);
}
protected abstract AbstractAmazonS3Operations getS3OperationsImplementation();
}

View File

@@ -1,87 +0,0 @@
/*
* 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.s3.core;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.integration.aws.core.PropertiesAWSCredentials;
/**
* The test class for the {@link DefaultAmazonS3Operations}, the default implementation that
* uses the AWS SDK to implement the functionality. The tests are present in the superclass
* {@link AbstractAmazonS3OperationsImplAWSTests}
*
* Please note that that this test needs connectivity with the AWS S3 service
* to be successfully executed. It is excluded from the maven's test execution by default
*
* 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 0.5
*
*/
@Ignore
public class DefaultAmazonS3OperationsAWSTests extends AbstractAmazonS3OperationsImplAWSTests {
private static DefaultAmazonS3Operations impl;
@BeforeClass
public static void setupS3Operations() throws Exception {
PropertiesAWSCredentials credentials =
new PropertiesAWSCredentials("classpath:awscredentials.properties");
credentials.afterPropertiesSet();
impl = new DefaultAmazonS3Operations(credentials);
}
/**
* Sets the thread pool executor to a non null value, execution should
* complete successfully
*
*/
@Ignore
@Test
public void withNonNullThreadPoolExecutor() {
ThreadPoolExecutor executor = (ThreadPoolExecutor)Executors.newFixedThreadPool(10);
impl.setThreadPoolExecutor(executor);
Assert.assertEquals(executor, impl.getThreadPoolExecutor());
}
/**
* Sets the thread pool executor to a null value, should throw an
* {@link IllegalArgumentException}
*/
@Ignore
@Test(expected=IllegalArgumentException.class)
public void withNullThreadPoolExecutor() {
impl.setThreadPoolExecutor(null);
}
@Override
protected AbstractAmazonS3Operations getS3OperationsImplementation() {
return impl;
}
}

View File

@@ -1,220 +0,0 @@
/*
* Copyright 2002-2014 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 org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandlingException;
/**
* The test class for {@link AmazonSESMessageHandler}
*
* @author Amol Nayak
* @author Rob Harrop
*
* @since 0.5
*
*/
public class AmazonSESMessageHandlerTests {
private static final List<SimpleMailMessage> messages = new ArrayList<SimpleMailMessage>();
private static final List<MimeMessage> mimeMessages = new ArrayList<MimeMessage>();
private static AmazonSESMessageHandler handler;
@BeforeClass
public static void setupAmazonSESMailSender() {
JavaMailSender sender = mock(JavaMailSender.class);
doAnswer(new Answer<Void>() {
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<Void>() {
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<Void>() {
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<Void>() {
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());
// }
}

View File

@@ -1,101 +0,0 @@
/*
* Copyright 2002-2014 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 java.util.Properties;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.MessageHandler;
import com.amazonaws.services.simpleemail.AWSJavaMailTransport;
/**
* The test class for the AmazonSESOutboundAdapterParser
*
* @author Amol Nayak
* @author Rob Harrop
*
* @since 0.5
*
*/
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);
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));
}
/**
* 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");
}
}

View File

@@ -1,131 +0,0 @@
/*
* 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.internet.MimeMessage;
import org.junit.BeforeClass;
import org.junit.Ignore;
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 0.5
*
*/
@Ignore
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 {
MimeMessageHelper helper = new MimeMessageHelper(sender.createMimeMessage());
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 {
MimeMessage[] messages = new MimeMessage[2];
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(sender.createMimeMessage());
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);
}
}

View File

@@ -1,8 +0,0 @@
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

View File

@@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:int-aws="http://www.springframework.org/schema/integration/aws/"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/aws http://www.springframework.org/schema/integration/aws/spring-integration-aws.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<integration:channel id="input"/>
<int-aws:s3-outbound-channel-adapter
id="withCustomNameGenerator"
channel="input"
bucket="TestBucket"
accessKey="dummy"
secretKey="dummy"
remote-directory="/"
file-name-generator="customFileNameGenerator"
file-name-generation-expression="headers['name']"
auto-startup="false"/>
<bean id="customFileNameGenerator"
class="org.springframework.integration.aws.s3.config.xml.AmazonS3OutboundChannelAdapterParserTests.DummyFileNameGenerator"/>
</beans>

View File

@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:int-aws="http://www.springframework.org/schema/integration/aws"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/aws http://www.springframework.org/schema/integration/aws/spring-integration-aws.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<integration:channel id="input"/>
<int-aws:s3-outbound-channel-adapter
id="withCustomNameGenerator"
channel="input"
s3-operations="customOps"
bucket="TestBucket"
accessKey="dummy"
secretKey="dummy"
multipart-upload-threshold="5120"
remote-directory="/"
file-name-generation-expression="headers['name']"
auto-startup="false"/>
<bean id="customOps"
class="org.springframework.integration.aws.s3.config.xml.AmazonS3OutboundChannelAdapterParserTests.DummyS3Operations"/>
</beans>

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:int-aws="http://www.springframework.org/schema/integration/aws"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/aws http://www.springframework.org/schema/integration/aws/spring-integration-aws.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<integration:channel id="input"/>
<int-aws:s3-outbound-channel-adapter
id="withDefaultServices"
channel="input"
bucket="TestBucket"
accessKey="dummy"
secretKey="dummy"
remote-directory-expression="headers['remoteDirectory']"
multipart-upload-threshold="1024"/>
</beans>

View File

@@ -1,65 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:int-aws="http://www.springframework.org/schema/integration/aws"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/aws http://www.springframework.org/schema/integration/aws/spring-integration-aws.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<bean id="localDirectory" class="java.lang.String">
<constructor-arg value="#{T(java.lang.System).getProperty('java.io.tmpdir')}"/>
</bean>
<integration:channel id="inbound"/>
<int-aws:s3-inbound-channel-adapter
id="validInbound"
bucket="TestBucket"
propertiesFile="testawscredentials.properties"
temporary-suffix=".temp"
local-directory-expression="@localDirectory"
remote-directory="remote"
accept-sub-folders="true"
max-objects-per-batch="100"
file-name-regex="[A-Za-z0-9]+\\.txt"
channel="inbound"
auto-startup="false">
<integration:poller fixed-rate="1000"/>
</int-aws:s3-inbound-channel-adapter>
<int-aws:s3-inbound-channel-adapter
id="withAWSEndpoint"
bucket="TestBucket"
propertiesFile="testawscredentials.properties"
aws-endpoint="s3-eu-west-1.amazonaws.com"
temporary-suffix=".temp"
local-directory-expression="@localDirectory"
remote-directory="remote"
accept-sub-folders="true"
channel="inbound"
auto-startup="false">
<integration:poller fixed-rate="1000"/>
</int-aws:s3-inbound-channel-adapter>
<int-aws:s3-inbound-channel-adapter
id="validInboundWithCustomOps"
bucket="TestBucket"
s3-operations="customOps"
propertiesFile="testawscredentials.properties"
temporary-suffix=".temp"
local-directory-expression="@localDirectory"
remote-directory="remote"
accept-sub-folders="true"
max-objects-per-batch="100"
file-name-regex="[A-Za-z0-9]+\\.txt"
channel="inbound"
auto-startup="false">
<integration:poller fixed-rate="1000"/>
</int-aws:s3-inbound-channel-adapter>
<bean id="customOps"
class="org.springframework.integration.aws.s3.config.xml.AmazonS3DummyOperations"/>
</beans>

View File

@@ -1,75 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:int-aws="http://www.springframework.org/schema/integration/aws"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/aws http://www.springframework.org/schema/integration/aws/spring-integration-aws.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<integration:channel id="input"/>
<int-aws:s3-outbound-channel-adapter
id="withCustomService"
channel="input"
bucket="TestBucket"
accessKey="dummy"
secretKey="dummy"
s3-operations="customOps"
remote-directory="/"
auto-startup="false"/>
<int-aws:s3-outbound-channel-adapter
id="withDefaultServices"
channel="input"
bucket="TestBucket"
accessKey="dummy"
secretKey="dummy"
remote-directory-expression="headers['remoteDirectory']"
charset="US-ASCII"
thread-pool-executor="executor"
multipart-upload-threshold="5120"
temporary-suffix=".write"
file-name-generation-expression="headers['name']"/>
<int-aws:s3-outbound-channel-adapter
id="withCustomNameGenerator"
channel="input"
bucket="TestBucket"
accessKey="dummy"
secretKey="dummy"
remote-directory="/"
file-name-generator="customFileNameGenerator"
auto-startup="false"/>
<int-aws:s3-outbound-channel-adapter
id="withCustomEndpoint"
aws-endpoint="http://s3-eu-west-1.amazonaws.com"
channel="input"
bucket="TestBucket"
accessKey="dummy"
secretKey="dummy"
remote-directory="/"
auto-startup="false"/>
<bean id="queue" class="java.util.concurrent.ArrayBlockingQueue">
<constructor-arg value="10"/>
</bean>
<bean id="executor" class="java.util.concurrent.ThreadPoolExecutor">
<constructor-arg index="0" value="5"/>
<constructor-arg index="1" value="10"/>
<constructor-arg index="2" value="10"/>
<constructor-arg index="3" value="MILLISECONDS"/>
<constructor-arg index="4" ref="queue"/>
</bean>
<bean id="customOps"
class="org.springframework.integration.aws.s3.config.xml.AmazonS3DummyOperations"/>
<bean id="customFileNameGenerator"
class="org.springframework.integration.aws.s3.config.xml.AmazonS3OutboundChannelAdapterParserTests.DummyFileNameGenerator"/>
</beans>

View File

@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:int-aws="http://www.springframework.org/schema/integration/aws"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/aws http://www.springframework.org/schema/integration/aws/spring-integration-aws.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<integration:channel id="inbound"/>
<int-aws:s3-inbound-channel-adapter
id="validInbound"
bucket="TestBucket"
propertiesFile="testawscredentials.properties"
temporary-suffix=".temp"
remote-directory="remote"
accept-sub-folders="true"
max-objects-per-batch="100"
file-name-regex="[A-Za-z0-9]+\\.txt"
channel="inbound">
<integration:poller fixed-rate="1000"/>
</int-aws:s3-inbound-channel-adapter>
</beans>

View File

@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:int-aws="http://www.springframework.org/schema/integration/aws"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/aws http://www.springframework.org/schema/integration/aws/spring-integration-aws.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<integration:channel id="outboundAdapterChannel"/>
<int-aws:ses-outbound-channel-adapter id="validDefinition"
propertiesFile="classpath:testawscredentials.properties"
accessKey="propDummy"
secretKey="propDummy"
channel="outboundAdapterChannel"/>
</beans>

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:int-aws="http://www.springframework.org/schema/integration/aws"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/aws http://www.springframework.org/schema/integration/aws/spring-integration-aws.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<integration:channel id="outboundAdapterChannel"/>
<bean id="credentials" class="org.springframework.integration.aws.core.BasicAWSCredentials">
<constructor-arg name="accessKey" value="dummy"/>
<constructor-arg name="secretKey" value="dummy"/>
</bean>
<int-aws:ses-outbound-channel-adapter id="validDefinition"
credentials-ref="credentials"
channel="outboundAdapterChannel"/>
</beans>

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:int-aws="http://www.springframework.org/schema/integration/aws"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/aws http://www.springframework.org/schema/integration/aws/spring-integration-aws.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<integration:channel id="outboundAdapterChannel"/>
<int-aws:ses-outbound-channel-adapter id="validDefinition"
propertiesFile="classpath:testawscredentials.properties" channel="outboundAdapterChannel"/>
</beans>

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:int-aws="http://www.springframework.org/schema/integration/aws"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/aws http://www.springframework.org/schema/integration/aws/spring-integration-aws.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<integration:channel id="outboundAdapterChannel"/>
<int-aws:ses-outbound-channel-adapter id="validDefinition"
accessKey="dummy"
secretKey="dummy"
channel="outboundAdapterChannel"/>
</beans>

View File

@@ -1,2 +0,0 @@
accessKey=dummy
secretKey=dummy