3344 lines
155 KiB
HTML
3344 lines
155 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="generator" content="Asciidoctor 1.5.2">
|
||
<meta name="author" content="Agim Emruli, Alain Sahli">
|
||
<title>Spring Cloud AWS</title>
|
||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400">
|
||
<style>
|
||
/* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */
|
||
/* Remove the comments around the @import statement below when using this as a custom stylesheet */
|
||
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400";*/
|
||
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}
|
||
audio,canvas,video{display:inline-block}
|
||
audio:not([controls]){display:none;height:0}
|
||
[hidden],template{display:none}
|
||
script{display:none!important}
|
||
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
|
||
body{margin:0}
|
||
a{background:transparent}
|
||
a:focus{outline:thin dotted}
|
||
a:active,a:hover{outline:0}
|
||
h1{font-size:2em;margin:.67em 0}
|
||
abbr[title]{border-bottom:1px dotted}
|
||
b,strong{font-weight:bold}
|
||
dfn{font-style:italic}
|
||
hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}
|
||
mark{background:#ff0;color:#000}
|
||
code,kbd,pre,samp{font-family:monospace;font-size:1em}
|
||
pre{white-space:pre-wrap}
|
||
q{quotes:"\201C" "\201D" "\2018" "\2019"}
|
||
small{font-size:80%}
|
||
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
|
||
sup{top:-.5em}
|
||
sub{bottom:-.25em}
|
||
img{border:0}
|
||
svg:not(:root){overflow:hidden}
|
||
figure{margin:0}
|
||
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
|
||
legend{border:0;padding:0}
|
||
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
|
||
button,input{line-height:normal}
|
||
button,select{text-transform:none}
|
||
button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
|
||
button[disabled],html input[disabled]{cursor:default}
|
||
input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}
|
||
input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}
|
||
input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}
|
||
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
|
||
textarea{overflow:auto;vertical-align:top}
|
||
table{border-collapse:collapse;border-spacing:0}
|
||
*,*:before,*:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
|
||
html,body{font-size:100%}
|
||
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto}
|
||
a:hover{cursor:pointer}
|
||
img,object,embed{max-width:100%;height:auto}
|
||
object,embed{height:100%}
|
||
img{-ms-interpolation-mode:bicubic}
|
||
#map_canvas img,#map_canvas embed,#map_canvas object,.map_canvas img,.map_canvas embed,.map_canvas object{max-width:none!important}
|
||
.left{float:left!important}
|
||
.right{float:right!important}
|
||
.text-left{text-align:left!important}
|
||
.text-right{text-align:right!important}
|
||
.text-center{text-align:center!important}
|
||
.text-justify{text-align:justify!important}
|
||
.hide{display:none}
|
||
.antialiased,body{-webkit-font-smoothing:antialiased}
|
||
img{display:inline-block;vertical-align:middle}
|
||
textarea{height:auto;min-height:50px}
|
||
select{width:100%}
|
||
p.lead,.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{font-size:1.21875em;line-height:1.6}
|
||
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
|
||
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}
|
||
a{color:#2156a5;text-decoration:underline;line-height:inherit}
|
||
a:hover,a:focus{color:#1d4b8f}
|
||
a img{border:none}
|
||
p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
|
||
p aside{font-size:.875em;line-height:1.35;font-style:italic}
|
||
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
|
||
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
|
||
h1{font-size:2.125em}
|
||
h2{font-size:1.6875em}
|
||
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
|
||
h4,h5{font-size:1.125em}
|
||
h6{font-size:1em}
|
||
hr{border:solid #ddddd8;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}
|
||
em,i{font-style:italic;line-height:inherit}
|
||
strong,b{font-weight:bold;line-height:inherit}
|
||
small{font-size:60%;line-height:inherit}
|
||
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
|
||
ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
|
||
ul,ol,ul.no-bullet,ol.no-bullet{margin-left:1.5em}
|
||
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}
|
||
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
|
||
ul.square{list-style-type:square}
|
||
ul.circle{list-style-type:circle}
|
||
ul.disc{list-style-type:disc}
|
||
ul.no-bullet{list-style:none}
|
||
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
|
||
dl dt{margin-bottom:.3125em;font-weight:bold}
|
||
dl dd{margin-bottom:1.25em}
|
||
abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}
|
||
abbr{text-transform:none}
|
||
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
|
||
blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}
|
||
blockquote cite:before{content:"\2014 \0020"}
|
||
blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}
|
||
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
|
||
@media only screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
|
||
h1{font-size:2.75em}
|
||
h2{font-size:2.3125em}
|
||
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
|
||
h4{font-size:1.4375em}}table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}
|
||
table thead,table tfoot{background:#f7f8f7;font-weight:bold}
|
||
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
|
||
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
|
||
table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7}
|
||
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}
|
||
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
|
||
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
|
||
.clearfix:before,.clearfix:after,.float-group:before,.float-group:after{content:" ";display:table}
|
||
.clearfix:after,.float-group:after{clear:both}
|
||
*:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
|
||
pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeSpeed}
|
||
.keyseq{color:rgba(51,51,51,.8)}
|
||
kbd{display:inline-block;color:rgba(0,0,0,.8);font-size:.75em;line-height:1.4;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:-.15em .15em 0 .15em;padding:.2em .6em .2em .5em;vertical-align:middle;white-space:nowrap}
|
||
.keyseq kbd:first-child{margin-left:0}
|
||
.keyseq kbd:last-child{margin-right:0}
|
||
.menuseq,.menu{color:rgba(0,0,0,.8)}
|
||
b.button:before,b.button:after{position:relative;top:-1px;font-weight:400}
|
||
b.button:before{content:"[";padding:0 3px 0 2px}
|
||
b.button:after{content:"]";padding:0 2px 0 3px}
|
||
p a>code:hover{color:rgba(0,0,0,.9)}
|
||
#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
|
||
#header:before,#header:after,#content:before,#content:after,#footnotes:before,#footnotes:after,#footer:before,#footer:after{content:" ";display:table}
|
||
#header:after,#content:after,#footnotes:after,#footer:after{clear:both}
|
||
#content{margin-top:1.25em}
|
||
#content:before{content:none}
|
||
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
|
||
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #ddddd8}
|
||
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #ddddd8;padding-bottom:8px}
|
||
#header .details{border-bottom:1px solid #ddddd8;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}
|
||
#header .details span:first-child{margin-left:-.125em}
|
||
#header .details span.email a{color:rgba(0,0,0,.85)}
|
||
#header .details br{display:none}
|
||
#header .details br+span:before{content:"\00a0\2013\00a0"}
|
||
#header .details br+span.author:before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
|
||
#header .details br+span#revremark:before{content:"\00a0|\00a0"}
|
||
#header #revnumber{text-transform:capitalize}
|
||
#header #revnumber:after{content:"\00a0"}
|
||
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #ddddd8;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
|
||
#toc{border-bottom:1px solid #efefed;padding-bottom:.5em}
|
||
#toc>ul{margin-left:.125em}
|
||
#toc ul.sectlevel0>li>a{font-style:italic}
|
||
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
|
||
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
|
||
#toc a{text-decoration:none}
|
||
#toc a:active{text-decoration:underline}
|
||
#toctitle{color:#7a2518;font-size:1.2em}
|
||
@media only screen and (min-width:768px){#toctitle{font-size:1.375em}
|
||
body.toc2{padding-left:15em;padding-right:0}
|
||
#toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #efefed;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
|
||
#toc.toc2 #toctitle{margin-top:0;font-size:1.2em}
|
||
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
|
||
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
|
||
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
|
||
body.toc2.toc-right{padding-left:0;padding-right:15em}
|
||
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #efefed;left:auto;right:0}}@media only screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
|
||
#toc.toc2{width:20em}
|
||
#toc.toc2 #toctitle{font-size:1.375em}
|
||
#toc.toc2>ul{font-size:.95em}
|
||
#toc.toc2 ul ul{padding-left:1.25em}
|
||
body.toc2.toc-right{padding-left:0;padding-right:20em}}#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
|
||
#content #toc>:first-child{margin-top:0}
|
||
#content #toc>:last-child{margin-bottom:0}
|
||
#footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em}
|
||
#footer-text{color:rgba(255,255,255,.8);line-height:1.44}
|
||
.sect1{padding-bottom:.625em}
|
||
@media only screen and (min-width:768px){.sect1{padding-bottom:1.25em}}.sect1+.sect1{border-top:1px solid #efefed}
|
||
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
|
||
#content h1>a.anchor:before,h2>a.anchor:before,h3>a.anchor:before,#toctitle>a.anchor:before,.sidebarblock>.content>.title>a.anchor:before,h4>a.anchor:before,h5>a.anchor:before,h6>a.anchor:before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
|
||
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
|
||
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
|
||
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
|
||
.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
|
||
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
|
||
table.tableblock>caption.title{white-space:nowrap;overflow:visible;max-width:0}
|
||
.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{color:rgba(0,0,0,.85)}
|
||
table.tableblock #preamble>.sectionbody>.paragraph:first-of-type p{font-size:inherit}
|
||
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
|
||
.admonitionblock>table td.icon{text-align:center;width:80px}
|
||
.admonitionblock>table td.icon img{max-width:none}
|
||
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
|
||
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #ddddd8;color:rgba(0,0,0,.6)}
|
||
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
|
||
.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}
|
||
.exampleblock>.content>:first-child{margin-top:0}
|
||
.exampleblock>.content>:last-child{margin-bottom:0}
|
||
.sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
|
||
.sidebarblock>:first-child{margin-top:0}
|
||
.sidebarblock>:last-child{margin-bottom:0}
|
||
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
|
||
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
|
||
.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8}
|
||
.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1}
|
||
.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;padding:1em;font-size:.8125em}
|
||
.literalblock pre.nowrap,.literalblock pre[class].nowrap,.listingblock pre.nowrap,.listingblock pre[class].nowrap{overflow-x:auto;white-space:pre;word-wrap:normal}
|
||
@media only screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}@media only screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}
|
||
.listingblock pre.highlightjs{padding:0}
|
||
.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}
|
||
.listingblock pre.prettyprint{border-width:0}
|
||
.listingblock>.content{position:relative}
|
||
.listingblock code[data-lang]:before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999}
|
||
.listingblock:hover code[data-lang]:before{display:block}
|
||
.listingblock.terminal pre .command:before{content:attr(data-prompt);padding-right:.5em;color:#999}
|
||
.listingblock.terminal pre .command:not([data-prompt]):before{content:"$"}
|
||
table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:none}
|
||
table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0}
|
||
table.pyhltable td.code{padding-left:.75em;padding-right:0}
|
||
pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #ddddd8}
|
||
pre.pygments .lineno{display:inline-block;margin-right:.25em}
|
||
table.pyhltable .linenodiv{background:none!important;padding-right:0!important}
|
||
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
|
||
.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em}
|
||
.quoteblock blockquote,.quoteblock blockquote p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
|
||
.quoteblock blockquote{margin:0;padding:0;border:0}
|
||
.quoteblock blockquote:before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
|
||
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
|
||
.quoteblock .attribution{margin-top:.5em;margin-right:.5ex;text-align:right}
|
||
.quoteblock .quoteblock{margin-left:0;margin-right:0;padding:.5em 0;border-left:3px solid rgba(0,0,0,.6)}
|
||
.quoteblock .quoteblock blockquote{padding:0 0 0 .75em}
|
||
.quoteblock .quoteblock blockquote:before{display:none}
|
||
.verseblock{margin:0 1em 1.25em 1em}
|
||
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
|
||
.verseblock pre strong{font-weight:400}
|
||
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
|
||
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
|
||
.quoteblock .attribution br,.verseblock .attribution br{display:none}
|
||
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.05em;color:rgba(0,0,0,.6)}
|
||
.quoteblock.abstract{margin:0 0 1.25em 0;display:block}
|
||
.quoteblock.abstract blockquote,.quoteblock.abstract blockquote p{text-align:left;word-spacing:0}
|
||
.quoteblock.abstract blockquote:before,.quoteblock.abstract blockquote p:first-of-type:before{display:none}
|
||
table.tableblock{max-width:100%;border-collapse:separate}
|
||
table.tableblock td>.paragraph:last-child p>p:last-child,table.tableblock th>p:last-child,table.tableblock td>p:last-child{margin-bottom:0}
|
||
table.spread{width:100%}
|
||
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
|
||
table.grid-all th.tableblock,table.grid-all td.tableblock{border-width:0 1px 1px 0}
|
||
table.grid-all tfoot>tr>th.tableblock,table.grid-all tfoot>tr>td.tableblock{border-width:1px 1px 0 0}
|
||
table.grid-cols th.tableblock,table.grid-cols td.tableblock{border-width:0 1px 0 0}
|
||
table.grid-all *>tr>.tableblock:last-child,table.grid-cols *>tr>.tableblock:last-child{border-right-width:0}
|
||
table.grid-rows th.tableblock,table.grid-rows td.tableblock{border-width:0 0 1px 0}
|
||
table.grid-all tbody>tr:last-child>th.tableblock,table.grid-all tbody>tr:last-child>td.tableblock,table.grid-all thead:last-child>tr>th.tableblock,table.grid-rows tbody>tr:last-child>th.tableblock,table.grid-rows tbody>tr:last-child>td.tableblock,table.grid-rows thead:last-child>tr>th.tableblock{border-bottom-width:0}
|
||
table.grid-rows tfoot>tr>th.tableblock,table.grid-rows tfoot>tr>td.tableblock{border-width:1px 0 0 0}
|
||
table.frame-all{border-width:1px}
|
||
table.frame-sides{border-width:0 1px}
|
||
table.frame-topbot{border-width:1px 0}
|
||
th.halign-left,td.halign-left{text-align:left}
|
||
th.halign-right,td.halign-right{text-align:right}
|
||
th.halign-center,td.halign-center{text-align:center}
|
||
th.valign-top,td.valign-top{vertical-align:top}
|
||
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
|
||
th.valign-middle,td.valign-middle{vertical-align:middle}
|
||
table thead th,table tfoot th{font-weight:bold}
|
||
tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}
|
||
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
|
||
p.tableblock>code:only-child{background:none;padding:0}
|
||
p.tableblock{font-size:1em}
|
||
td>div.verse{white-space:pre}
|
||
ol{margin-left:1.75em}
|
||
ul li ol{margin-left:1.5em}
|
||
dl dd{margin-left:1.125em}
|
||
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
|
||
ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
|
||
ul.unstyled,ol.unnumbered,ul.checklist,ul.none{list-style-type:none}
|
||
ul.unstyled,ol.unnumbered,ul.checklist{margin-left:.625em}
|
||
ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1em;font-size:.85em}
|
||
ul.checklist li>p:first-child>input[type="checkbox"]:first-child{width:1em;position:relative;top:1px}
|
||
ul.inline{margin:0 auto .625em auto;margin-left:-1.375em;margin-right:0;padding:0;list-style:none;overflow:hidden}
|
||
ul.inline>li{list-style:none;float:left;margin-left:1.375em;display:block}
|
||
ul.inline>li>*{display:block}
|
||
.unstyled dl dt{font-weight:400;font-style:normal}
|
||
ol.arabic{list-style-type:decimal}
|
||
ol.decimal{list-style-type:decimal-leading-zero}
|
||
ol.loweralpha{list-style-type:lower-alpha}
|
||
ol.upperalpha{list-style-type:upper-alpha}
|
||
ol.lowerroman{list-style-type:lower-roman}
|
||
ol.upperroman{list-style-type:upper-roman}
|
||
ol.lowergreek{list-style-type:lower-greek}
|
||
.hdlist>table,.colist>table{border:0;background:none}
|
||
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
|
||
td.hdlist1{padding-right:.75em;font-weight:bold}
|
||
td.hdlist1,td.hdlist2{vertical-align:top}
|
||
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
|
||
.colist>table tr>td:first-of-type{padding:0 .75em;line-height:1}
|
||
.colist>table tr>td:last-of-type{padding:.25em 0}
|
||
.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}
|
||
.imageblock.left,.imageblock[style*="float: left"]{margin:.25em .625em 1.25em 0}
|
||
.imageblock.right,.imageblock[style*="float: right"]{margin:.25em 0 1.25em .625em}
|
||
.imageblock>.title{margin-bottom:0}
|
||
.imageblock.thumb,.imageblock.th{border-width:6px}
|
||
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
|
||
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
|
||
.image.left{margin-right:.625em}
|
||
.image.right{margin-left:.625em}
|
||
a.image{text-decoration:none}
|
||
span.footnote,span.footnoteref{vertical-align:super;font-size:.875em}
|
||
span.footnote a,span.footnoteref a{text-decoration:none}
|
||
span.footnote a:active,span.footnoteref a:active{text-decoration:underline}
|
||
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
|
||
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em 0;border-width:1px 0 0 0}
|
||
#footnotes .footnote{padding:0 .375em;line-height:1.3;font-size:.875em;margin-left:1.2em;text-indent:-1.2em;margin-bottom:.2em}
|
||
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none}
|
||
#footnotes .footnote:last-of-type{margin-bottom:0}
|
||
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
|
||
.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}
|
||
.gist .file-data>table td.line-data{width:99%}
|
||
div.unbreakable{page-break-inside:avoid}
|
||
.big{font-size:larger}
|
||
.small{font-size:smaller}
|
||
.underline{text-decoration:underline}
|
||
.overline{text-decoration:overline}
|
||
.line-through{text-decoration:line-through}
|
||
.aqua{color:#00bfbf}
|
||
.aqua-background{background-color:#00fafa}
|
||
.black{color:#000}
|
||
.black-background{background-color:#000}
|
||
.blue{color:#0000bf}
|
||
.blue-background{background-color:#0000fa}
|
||
.fuchsia{color:#bf00bf}
|
||
.fuchsia-background{background-color:#fa00fa}
|
||
.gray{color:#606060}
|
||
.gray-background{background-color:#7d7d7d}
|
||
.green{color:#006000}
|
||
.green-background{background-color:#007d00}
|
||
.lime{color:#00bf00}
|
||
.lime-background{background-color:#00fa00}
|
||
.maroon{color:#600000}
|
||
.maroon-background{background-color:#7d0000}
|
||
.navy{color:#000060}
|
||
.navy-background{background-color:#00007d}
|
||
.olive{color:#606000}
|
||
.olive-background{background-color:#7d7d00}
|
||
.purple{color:#600060}
|
||
.purple-background{background-color:#7d007d}
|
||
.red{color:#bf0000}
|
||
.red-background{background-color:#fa0000}
|
||
.silver{color:#909090}
|
||
.silver-background{background-color:#bcbcbc}
|
||
.teal{color:#006060}
|
||
.teal-background{background-color:#007d7d}
|
||
.white{color:#bfbfbf}
|
||
.white-background{background-color:#fafafa}
|
||
.yellow{color:#bfbf00}
|
||
.yellow-background{background-color:#fafa00}
|
||
span.icon>.fa{cursor:default}
|
||
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
|
||
.admonitionblock td.icon .icon-note:before{content:"\f05a";color:#19407c}
|
||
.admonitionblock td.icon .icon-tip:before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
|
||
.admonitionblock td.icon .icon-warning:before{content:"\f071";color:#bf6900}
|
||
.admonitionblock td.icon .icon-caution:before{content:"\f06d";color:#bf3400}
|
||
.admonitionblock td.icon .icon-important:before{content:"\f06a";color:#bf0000}
|
||
.conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
|
||
.conum[data-value] *{color:#fff!important}
|
||
.conum[data-value]+b{display:none}
|
||
.conum[data-value]:after{content:attr(data-value)}
|
||
pre .conum[data-value]{position:relative;top:-.125em}
|
||
b.conum *{color:inherit!important}
|
||
.conum:not([data-value]):empty{display:none}
|
||
h1,h2{letter-spacing:-.01em}
|
||
dt,th.tableblock,td.content{text-rendering:optimizeLegibility}
|
||
p,td.content{letter-spacing:-.01em}
|
||
p strong,td.content strong{letter-spacing:-.005em}
|
||
p,blockquote,dt,td.content{font-size:1.0625rem}
|
||
p{margin-bottom:1.25rem}
|
||
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
|
||
.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}
|
||
.print-only{display:none!important}
|
||
@media print{@page{margin:1.25cm .75cm}
|
||
*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}
|
||
a{color:inherit!important;text-decoration:underline!important}
|
||
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
|
||
a[href^="http:"]:not(.bare):after,a[href^="https:"]:not(.bare):after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
|
||
abbr[title]:after{content:" (" attr(title) ")"}
|
||
pre,blockquote,tr,img{page-break-inside:avoid}
|
||
thead{display:table-header-group}
|
||
img{max-width:100%!important}
|
||
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
|
||
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
|
||
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
|
||
#toc{border-bottom:1px solid #ddddd8!important;padding-bottom:0!important}
|
||
.sect1{padding-bottom:0!important}
|
||
.sect1+.sect1{border:0!important}
|
||
#header>h1:first-child{margin-top:1.25rem}
|
||
body.book #header{text-align:center}
|
||
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em 0}
|
||
body.book #header .details{border:0!important;display:block;padding:0!important}
|
||
body.book #header .details span:first-child{margin-left:0!important}
|
||
body.book #header .details br{display:block}
|
||
body.book #header .details br+span:before{content:none!important}
|
||
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
|
||
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
|
||
.listingblock code[data-lang]:before{display:block}
|
||
#footer{background:none!important;padding:0 .9375em}
|
||
#footer-text{color:rgba(0,0,0,.6)!important;font-size:.9em}
|
||
.hide-on-print{display:none!important}
|
||
.print-only{display:block!important}
|
||
.hide-for-print{display:none!important}
|
||
.show-for-print{display:inherit!important}}
|
||
</style>
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.min.css">
|
||
</head>
|
||
<body class="article">
|
||
<div id="header">
|
||
<h1>Spring Cloud AWS</h1>
|
||
<div class="details">
|
||
<span id="author" class="author">Agim Emruli</span><br>
|
||
<span id="author2" class="author">Alain Sahli</span><br>
|
||
</div>
|
||
<div id="toc" class="toc">
|
||
<div id="toctitle">Table of Contents</div>
|
||
<ul class="sectlevel1">
|
||
<li><a href="#_using_amazon_web_services">1. Using Amazon Web Services</a></li>
|
||
<li><a href="#_basic_setup">2. Basic setup</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_spring_cloud_aws_maven_dependency_management">2.1. Spring Cloud AWS maven dependency management</a></li>
|
||
<li><a href="#_amazon_sdk_configuration">2.2. Amazon SDK configuration</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_sdk_credentials_configuration">2.2.1. SDK credentials configuration</a>
|
||
<ul class="sectlevel4">
|
||
<li><a href="#_simple_credentials_configuration">Simple credentials configuration</a></li>
|
||
<li><a href="#_instance_profile_configuration">Instance profile configuration</a></li>
|
||
<li><a href="#_mixing_both_security_configurations">Mixing both security configurations</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_region_configuration">2.2.2. Region configuration</a>
|
||
<ul class="sectlevel4">
|
||
<li><a href="#_explicit_region_configuration">Explicit region configuration</a></li>
|
||
<li><a href="#_automatic_region_configuration">Automatic region configuration</a></li>
|
||
<li><a href="#_service_specific_region_configuration">Service specific region configuration</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_spring_boot_auto_configuration">2.2.3. Spring Boot auto-configuration</a>
|
||
<ul class="sectlevel4">
|
||
<li><a href="#_maven_dependencies">Maven dependencies</a></li>
|
||
<li><a href="#_configuring_credentials">Configuring credentials</a></li>
|
||
<li><a href="#_configuring_region">Configuring region</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_cloud_environment">3. Cloud environment</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_retrieving_instance_metadata">3.1. Retrieving instance metadata</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_enabling_instance_metadata_support_with_xml">3.1.1. Enabling instance metadata support with XML</a></li>
|
||
<li><a href="#_enabling_instance_metadata_support_with_java">3.1.2. Enabling instance metadata support with Java</a></li>
|
||
<li><a href="#_enabling_instance_metadata_support_in_spring_boot">3.1.3. Enabling instance metadata support in Spring Boot</a></li>
|
||
<li><a href="#_using_instance_metadata">3.1.4. Using instance metadata</a></li>
|
||
<li><a href="#_using_instance_user_data">3.1.5. Using instance user data</a></li>
|
||
<li><a href="#_using_instance_tags">3.1.6. Using instance tags</a></li>
|
||
<li><a href="#_configuring_custom_ec2_client">3.1.7. Configuring custom EC2 client</a></li>
|
||
<li><a href="#_injecting_the_default_ec2_client">3.1.8. Injecting the default EC2 client</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_managing_cloud_environments">4. Managing cloud environments</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_automatic_cloudformation_configuration">4.1. Automatic CloudFormation configuration</a></li>
|
||
<li><a href="#_manual_cloudformation_configuration">4.2. Manual CloudFormation configuration</a></li>
|
||
<li><a href="#_cloudformation_configuration_with_java_config_classes">4.3. CloudFormation configuration with Java config classes</a></li>
|
||
<li><a href="#_cloudformation_configuration_in_spring_boot">4.4. CloudFormation configuration in Spring Boot</a></li>
|
||
<li><a href="#_manual_name_resolution">4.5. Manual name resolution</a></li>
|
||
<li><a href="#_stack_tags">4.6. Stack Tags</a></li>
|
||
<li><a href="#_using_custom_cloudformation_client">4.7. Using custom CloudFormation client</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_messaging">5. Messaging</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_configuring_messaging">5.1. Configuring messaging</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_maven_dependency_configuration">5.1.1. Maven dependency configuration</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_sqs_support">5.2. SQS support</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_sending_a_message">5.2.1. Sending a message</a>
|
||
<ul class="sectlevel4">
|
||
<li><a href="#_using_message_converters">Using message converters</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_receiving_a_message">5.2.2. Receiving a message</a></li>
|
||
<li><a href="#_annotation_driven_listener_endpoints">5.2.3. Annotation-driven listener endpoints</a>
|
||
<ul class="sectlevel4">
|
||
<li><a href="#_message_reply">Message reply</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_the_simplemessagelistenercontainerfactory">5.2.4. The SimpleMessageListenerContainerFactory</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_sns_support">5.3. SNS support</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_sending_a_message_2">5.3.1. Sending a message</a></li>
|
||
<li><a href="#_annotation_driven_http_notification_endpoint">5.3.2. Annotation-driven HTTP notification endpoint</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_using_cloudformation">5.4. Using CloudFormation</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_caching">6. Caching</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_configuring_dependencies_for_redis_caches">6.1. Configuring dependencies for Redis caches</a></li>
|
||
<li><a href="#_configuring_caching_with_xml">6.2. Configuring caching with XML</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_mixing_caches">6.2.1. Mixing caches</a></li>
|
||
<li><a href="#_defining_expiration">6.2.2. Defining expiration</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_configuring_caching_using_java_configuration">6.3. Configuring caching using Java configuration</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_configuring_expiry_time_for_caches">6.3.1. Configuring expiry time for caches</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_configuring_caching_in_spring_boot">6.4. Configuring caching in Spring Boot</a></li>
|
||
<li><a href="#_using_caching">6.5. Using caching</a></li>
|
||
<li><a href="#_memcached_client_implementation">6.6. Memcached client implementation</a></li>
|
||
<li><a href="#_using_cloudformation_2">6.7. Using CloudFormation</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_data_access_with_jdbc">7. Data Access with JDBC</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_configuring_data_source">7.1. Configuring data source</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_maven_dependency_configuration_2">7.1.1. Maven dependency configuration</a></li>
|
||
<li><a href="#_basic_data_source_configuration">7.1.2. Basic data source configuration</a></li>
|
||
<li><a href="#_data_source_pool_configuration">7.1.3. Data source pool configuration</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_configuring_data_source_with_java_config">7.2. Configuring data source with Java config</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_java_based_data_source_pool_configuration">7.2.1. Java based data source pool configuration</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_configuring_data_source_in_spring_boot">7.3. Configuring data source in Spring Boot</a></li>
|
||
<li><a href="#_read_replica_configuration">7.4. Read-replica configuration</a></li>
|
||
<li><a href="#_failover_support">7.5. Failover support</a></li>
|
||
<li><a href="#_cloudformation_support">7.6. CloudFormation support</a></li>
|
||
<li><a href="#_database_tags">7.7. Database tags</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_sending_mails">8. Sending mails</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_configuring_the_mail_sender">8.1. Configuring the mail sender</a></li>
|
||
<li><a href="#_sending_simple_mails">8.2. Sending simple mails</a></li>
|
||
<li><a href="#_sending_attachments">8.3. Sending attachments</a></li>
|
||
<li><a href="#_configuring_regions">8.4. Configuring regions</a></li>
|
||
<li><a href="#_authenticating_e_mails">8.5. Authenticating e-mails</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_resource_handling">9. Resource handling</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_configuring_the_resource_loader">9.1. Configuring the resource loader</a></li>
|
||
<li><a href="#_downloading_files">9.2. Downloading files</a></li>
|
||
<li><a href="#_uploading_files">9.3. Uploading files</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_uploading_multi_part_files">9.3.1. Uploading multi-part files</a></li>
|
||
<li><a href="#_uploading_with_the_transfermanager">9.3.2. Uploading with the TransferManager</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_searching_resources">9.4. Searching resources</a></li>
|
||
<li><a href="#_using_cloudformation_3">9.5. Using CloudFormation</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div id="content">
|
||
<div id="preamble">
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>Spring Cloud for Amazon Web Services, part of the Spring Cloud umbrella project, eases the integration with hosted Amazon Web Services. It offers a convenient way to interact with AWS provided services using well-known Spring idioms and APIs, such as the messaging or caching API. Developers can build their application around the hosted services without having to care about infrastructure or maintenance.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p><a href="https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/contributing-docs.adoc" class="bare">https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/contributing-docs.adoc</a></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_using_amazon_web_services">1. Using Amazon Web Services</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>Amazon provides a <a href="http://aws.amazon.com/sdk-for-java/">Java SDK</a> to issue requests for the all services provided by the
|
||
<a href="http://aws.amazon.com">Amazon Web Service</a> platform. Using the SDK, application developers still have to integrate the
|
||
SDK into their application with a considerable amount of infrastructure related code. Spring Cloud AWS provides application
|
||
developers already integrated Spring-based modules to consume services and avoid infrastructure related code as much as possible.
|
||
The Spring Cloud AWS module provides a module set so that application developers can arrange the dependencies based on
|
||
their needs for the particular services. The graphic below provides a general overview of all Spring Cloud AWS modules along
|
||
with the service support for the respective Spring Cloud AWS services.</p>
|
||
</div>
|
||
<div class="imageblock">
|
||
<div class="content">
|
||
<img src="images/overview.png" alt="Overview">
|
||
</div>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p><strong>Spring Cloud AWS Core</strong> is the core module of Spring Cloud AWS providing basic services for security and configuration
|
||
setup. Developers will not use this module directly but rather through other modules. The core module provides support for
|
||
cloud based environment configurations providing direct access to the instance based <a href="http://aws.amazon.com/ec2/">EC2</a>
|
||
metadata and the overall application stack specific <a href="http://aws.amazon.com/cloudformation/">CloudFormation</a> metadata.</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Spring Cloud AWS Context</strong> delivers access to the <a href="http://aws.amazon.com/s3/">Simple Storage Service</a> via the Spring
|
||
resource loader abstraction. Moreover developers can send e-mails using the <a href="http://aws.amazon.com/ses/">Simple E-Mail Service</a>
|
||
and the Spring mail abstraction. Further the developers can introduce declarative caching using the Spring caching support
|
||
and the <a href="http://aws.amazon.com/elasticache/">ElastiCache</a> caching service.</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Spring Cloud AWS JDBC</strong> provides automatic datasource lookup and configuration for the <a href="http://aws.amazon.com/rds/">Relational Database Service</a>
|
||
which can be used with JDBC or any other support data access technology by Spring.</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Spring Cloud AWS Messaging</strong> enables developers to receive and send messages with the <a href="http://aws.amazon.com/sqs/">Simple Queueing Service</a> for
|
||
point-to-point communication. Publish-subscribe messaging is supported with the integration of the <a href="http://aws.amazon.com/sns/">Simple Notification Service</a>.</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_basic_setup">2. Basic setup</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>Before using the Spring Cloud AWS module developers have to pick the dependencies and configure the Spring Cloud AWS module.
|
||
The next chapters describe the dependency management and also the basic configuration for the Spring AWS Cloud project.</p>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_spring_cloud_aws_maven_dependency_management">2.1. Spring Cloud AWS maven dependency management</h3>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS module dependencies can be used directly in <a href="http://maven.apache.org">Maven</a> with a direct configuration
|
||
of the particular module. The Spring Cloud AWS module includes all transitive dependencies for the Spring modules and
|
||
also the Amazon SDK that are needed to operate the modules. The general dependency configuration will look like this:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><dependencies>
|
||
<dependency>
|
||
<groupId>org.springframework.cloud</groupId>
|
||
<artifactId>spring-cloud-aws-context</artifactId>
|
||
<version>{spring-cloud-version}</version>
|
||
</dependency>
|
||
</dependencies></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Different modules can be included by replacing the module name with the respective one (e.g. <code>spring-cloud-aws-messaging</code>
|
||
instead of <code>spring-cloud-aws-context</code>)</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The example above works with the Maven Central repository. To use the Spring Maven repository (e.g. for milestones or
|
||
developer snapshots), you need to specify the repository location in your Maven configuration. For full releases:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><repositories>
|
||
<repository>
|
||
<id>io.spring.repo.maven.release</id>
|
||
<url>http://repo.spring.io/release/</url>
|
||
<snapshots><enabled>false</enabled></snapshots>
|
||
</repository>
|
||
</repositories></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>For milestones:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><repositories>
|
||
<repository>
|
||
<id>io.spring.repo.maven.milestone</id>
|
||
<url>http://repo.spring.io/milestone/</url>
|
||
<snapshots><enabled>false</enabled></snapshots>
|
||
</repository>
|
||
</repositories></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_amazon_sdk_configuration">2.2. Amazon SDK configuration</h3>
|
||
<div class="paragraph">
|
||
<p>The Spring Cloud AWS configuration is currently done using custom elements provided by Spring Cloud AWS namespaces.
|
||
JavaConfig will be supported soon. The configuration setup is done directly in Spring XML configuration files
|
||
so that the elements can be directly used. Each module of Spring Cloud AWS provides custom namespaces to allow the modular
|
||
use of the modules. A typical XML configuration to use Spring Cloud AWS is outlined below:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans xmlns="http://www.springframework.org/schema/beans"
|
||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
xmlns:aws-context="http://www.springframework.org/schema/cloud/aws/context"
|
||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||
http://www.springframework.org/schema/cloud/aws/context
|
||
http://www.springframework.org/schema/cloud/aws/context/spring-cloud-aws-context-1.0.xsd">
|
||
|
||
<aws-context:context-region region="..."/>
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_sdk_credentials_configuration">2.2.1. SDK credentials configuration</h4>
|
||
<div class="paragraph">
|
||
<p>In order to make calls to the Amazon Web Service the credentials must be configured for the the Amazon SDK. Spring Cloud AWS
|
||
provides support to configure an application context specific credentials that are used for <em>each</em> service call for requests done
|
||
by Spring Cloud AWS components. Therefore there must be <strong>exactly one</strong> configuration of the credentials for an entire application
|
||
context.</p>
|
||
</div>
|
||
<div class="admonitionblock tip">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-tip" title="Tip"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>The <code>com.amazonaws.auth.DefaultAWSCredentialsProviderChain</code> is used by all the clients if there is no dedicated credentials
|
||
provider defined. This will essentially use the following authentication information</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p>use the environment variables <code>AWS_ACCESS_KEY_ID</code> and <code>AWS_SECRET_ACCESS_KEY</code></p>
|
||
</li>
|
||
<li>
|
||
<p>use the system properties <code>aws.accessKeyId</code> and <code>aws.secretKey</code></p>
|
||
</li>
|
||
<li>
|
||
<p>use the user specific profile credentials file</p>
|
||
</li>
|
||
<li>
|
||
<p>use the instance profile credentials (see below)</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Based on the overall credentials policy there are different options to configure the credentials. The possible ones are described in
|
||
the following sub-chapters.</p>
|
||
</div>
|
||
<div class="sect4">
|
||
<h5 id="_simple_credentials_configuration">Simple credentials configuration</h5>
|
||
<div class="paragraph">
|
||
<p>Credentials for the Amazon SDK consist of an access key (which might be shared) and a secret key (which must <strong>not</strong> be shared). Both
|
||
security attributes can be configured using the XML namespaces for each Amazon SDK service created by the Spring Cloud AWS
|
||
module. The overall configuration looks like this</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans ...>
|
||
<aws-context:context-credentials>
|
||
<aws-context:simple-credentials access-key="AKIAIO" secret-key="wJalrXUtnFEMI/K7M" />
|
||
</aws-context:context-credentials>
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="admonitionblock caution">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-caution" title="Caution"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>The access-key and secret-key should be externalized into property files (e.g. Spring Boot application configuration)
|
||
and not be checked in into the source management system.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect4">
|
||
<h5 id="_instance_profile_configuration">Instance profile configuration</h5>
|
||
<div class="paragraph">
|
||
<p>An <a href="http://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html">instance profile configuration</a> allows to assign
|
||
a profile that is authorized by a role while starting an EC2 instance. All calls made from the EC2 instance are then authenticated
|
||
with the instance profile specific user role. Therefore there is no dedicated access-key and secret-key needed in the configuration.
|
||
The configuration for the instance profile in Spring Cloud AWS looks like this:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans ...>
|
||
<aws-context:context-credentials>
|
||
<aws-context:instance-profile-credentials/>
|
||
</aws-context:context-credentials>
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect4">
|
||
<h5 id="_mixing_both_security_configurations">Mixing both security configurations</h5>
|
||
<div class="paragraph">
|
||
<p>In some cases it is useful to combine both authentication strategies to allow the application to use the instance profile
|
||
with a fallback for an explicit access-key and secret-key configuration. This is useful if the application is tested inside
|
||
EC2 (e.g. on a test server) and locally for testing. The next snippet shows a combination of both security configurations.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans ...>
|
||
<aws-context:context-credentials>
|
||
<aws-context:instance-profile-credentials/>
|
||
<aws-context:simple-credentials access-key="${accessKey:}" secret-key="${secretKey:}"/>
|
||
</aws-context:context-credentials>
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="admonitionblock tip">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-tip" title="Tip"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>The access-key and secret-key are defined using a placeholder expressions along with a default value to avoid bootstrap
|
||
errors if the properties are not configured at all.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_region_configuration">2.2.2. Region configuration</h4>
|
||
<div class="paragraph">
|
||
<p>Amazon Web services are available in different <a href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html">regions</a>. Based
|
||
on the custom requirements, the user can host the application on different Amazon regions. The <code>spring-cloud-aws-context</code>
|
||
module provides a way to define the region for the entire application context.</p>
|
||
</div>
|
||
<div class="sect4">
|
||
<h5 id="_explicit_region_configuration">Explicit region configuration</h5>
|
||
<div class="paragraph">
|
||
<p>The region can be explicitly configured using an XML element. This is particularly useful if the region can not be automatically
|
||
derived because the application is not hosted on a EC2 instance (e.g. local testing) or the region must be manually overridden.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans ...>
|
||
<aws-context:context-region region="eu-west-1"/>
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="admonitionblock tip">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-tip" title="Tip"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>It is also allowed to use expressions or placeholders to externalize the configuration and ensure that the region can
|
||
be reconfigured with property files or system properties.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect4">
|
||
<h5 id="_automatic_region_configuration">Automatic region configuration</h5>
|
||
<div class="paragraph">
|
||
<p>If the application context is started inside an EC2 instance, then the region can automatically be fetched from the
|
||
<a href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html">instance metadata</a> and therefore must
|
||
not be configured statically. The configuration will look like this:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans ...>
|
||
<aws-context:context-region auto-detect="true" />
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect4">
|
||
<h5 id="_service_specific_region_configuration">Service specific region configuration</h5>
|
||
<div class="paragraph">
|
||
<p>A region can also be overridden for particular services if one application context consumes services from different regions.
|
||
The configuration can be done globally like described above and configured for each service with a region attribute.
|
||
The configuration might look like this for a database service (described later)</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans ...>
|
||
<aws-context:context-region region="eu-central-1" />
|
||
<jdbc:data-source ... region="eu-west-1" />
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="admonitionblock note">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-note" title="Note"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>While it is theoretically possible to use multiple regions per application, we strongly recommend to write applications that
|
||
are hosted only inside one region and split the application if it is hosted in different regions at the same time.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_spring_boot_auto_configuration">2.2.3. Spring Boot auto-configuration</h4>
|
||
<div class="paragraph">
|
||
<p>Following the Spring Cloud umbrella project, Spring Cloud AWS also provides dedicated Spring Boot support. Spring Cloud
|
||
AWS can be configured using Spring Boot properties and will also automatically guess any sensible configuration based on
|
||
the general setup.</p>
|
||
</div>
|
||
<div class="sect4">
|
||
<h5 id="_maven_dependencies">Maven dependencies</h5>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS provides a dedicated module to enable the Spring Boot support. That module must be added to the general
|
||
maven dependency inside the application. The typical configuration will look like this</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><dependencies>
|
||
<dependency>
|
||
<groupId>org.springframework.cloud</groupId>
|
||
<artifactId>spring-cloud-aws-autoconfigure</artifactId>
|
||
<version>{spring-cloud-version}</version>
|
||
</dependency>>
|
||
</dependencies></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Additional dependencies to enable particular features like messaging and JDBC have to be added. Spring Cloud AWS will
|
||
only configure classes that are available in the Spring Boot application’s classpath.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect4">
|
||
<h5 id="_configuring_credentials">Configuring credentials</h5>
|
||
<div class="paragraph">
|
||
<p>Spring Boot provides a standard way to define properties with property file or YAML configuration files. Spring Cloud
|
||
AWS provides support to configure the credential information with the Spring Boot application configuration files.
|
||
Spring Cloud AWS provides the following properties to configure the credentials setup for the whole application.</p>
|
||
</div>
|
||
<table class="tableblock frame-all grid-all spread">
|
||
<colgroup>
|
||
<col style="width: 33%;">
|
||
<col style="width: 33%;">
|
||
<col style="width: 33%;">
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th class="tableblock halign-left valign-top">property</th>
|
||
<th class="tableblock halign-left valign-top">example</th>
|
||
<th class="tableblock halign-left valign-top">description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">cloud.aws.credentials.accessKey</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">AKIAIOSFODNN7EXAMPLE</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">The access key to be used with a static provider</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">cloud.aws.credentials.secretKey</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">The secret key to be used with a static provider</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">cloud.aws.credentials.instanceProfile</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Configures an instance profile credentials provider with no further configuration</p></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="sect4">
|
||
<h5 id="_configuring_region">Configuring region</h5>
|
||
<div class="paragraph">
|
||
<p>Like for the credentials, the Spring Cloud AWS module also supports the configuration of the region inside the Spring
|
||
Boot configuration files. The region can be automatically detected or explicitly configured (e.g. in case of local tests
|
||
against the AWS cloud).</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The properties to configure the region are shown below</p>
|
||
</div>
|
||
<table class="tableblock frame-all grid-all spread">
|
||
<colgroup>
|
||
<col style="width: 33%;">
|
||
<col style="width: 33%;">
|
||
<col style="width: 33%;">
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th class="tableblock halign-left valign-top">property</th>
|
||
<th class="tableblock halign-left valign-top">example</th>
|
||
<th class="tableblock halign-left valign-top">description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">cloud.aws.region.auto</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Enables automatic region detection based on the EC2 meta data service</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">cloud.aws.region.static</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">eu-west-1</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Configures a static region for the application. Possible regions are (currently) us-east-1, us-west-1, us-west-2,
|
||
eu-west-1, eu-central-1, ap-southeast-1, ap-southeast-1, ap-northeast-1, sa-east-1, cn-north-1 and any custom region
|
||
configured with own region meta data</p></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_cloud_environment">3. Cloud environment</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>Applications often need environment specific configuration information, especially in changing environments like in the
|
||
Amazon cloud environment. Spring Cloud AWS provides a support to retrieve and use environment specific data inside the
|
||
application context using common Spring mechanisms like property placeholder or the Spring expression language.</p>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_retrieving_instance_metadata">3.1. Retrieving instance metadata</h3>
|
||
<div class="paragraph">
|
||
<p><a href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html">Instance metadata</a> are available inside an
|
||
EC2 environment. The metadata can be queried using a special HTTP address that provides the instance metadata. Spring Cloud
|
||
AWS enables application to access this metadata directly in expression or property placeholder without the need to call
|
||
an external HTTP service.</p>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_enabling_instance_metadata_support_with_xml">3.1.1. Enabling instance metadata support with XML</h4>
|
||
<div class="paragraph">
|
||
<p>The instance metadata retrieval support is enabled through an XML element like the standard property placeholder in Spring.
|
||
The following code sample demonstrates the activation of the instance metadata support inside an application context.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans ...>
|
||
<aws-context:context-instance-data />
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="admonitionblock tip">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-tip" title="Tip"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>Instance metadata can be retrieved without an authorized service call, therefore the configuration above does not require
|
||
any region or security specific configuration.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_enabling_instance_metadata_support_with_java">3.1.2. Enabling instance metadata support with Java</h4>
|
||
<div class="paragraph">
|
||
<p>The instance metadata can also be configured within a Java configuration class without the need for an XML configuration.
|
||
The next example shows a typical Spring <code>@Configuration</code> class that enables the instance metadata with the
|
||
<code>org.springframework.cloud.aws.context.config.annotation.EnableInstanceData</code></p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">@Configuration
|
||
@EnableContextInstanceData
|
||
public static class ApplicationConfiguration {
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_enabling_instance_metadata_support_in_spring_boot">3.1.3. Enabling instance metadata support in Spring Boot</h4>
|
||
<div class="paragraph">
|
||
<p>The instance metadata is automatically available in a Spring Boot application as a property source if the application
|
||
is running on an EC2 instance.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_using_instance_metadata">3.1.4. Using instance metadata</h4>
|
||
<div class="paragraph">
|
||
<p>Instance metadata can be used in XML, Java placeholders and expressions. The example below demonstrates the usage of
|
||
instance metadata inside an XML file using placeholders and also the expression referring to the special variable <code>environment</code></p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans ...>
|
||
<bean class="org.springframework.cloud.aws....SimpleConfigurationBean">
|
||
<property name="value1" value="#{environment.ami-id}" />
|
||
<property name="value2" value="#{environment.hostname}" />
|
||
<property name="value3" value="${instance-type}" />
|
||
<property name="value4" value="${instance-id}" />
|
||
</bean>
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Instance metadata can also be injected with the Spring <code>org.springframework.beans.factory.annotation.Value</code> annotation
|
||
directly into Java fields. The next example demonstrates the use of instance metadata inside a Spring bean.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">@Component
|
||
public class ApplicationInfoBean {
|
||
|
||
@Value("${ami-id:N/A}")
|
||
private String amiId;
|
||
|
||
@Value("${hostname:N/A}")
|
||
private String hostname;
|
||
|
||
@Value("${instance-type:N/A}")
|
||
private String instanceType;
|
||
|
||
@Value("${services/domain:N/A}")
|
||
private String serviceDomain;
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="admonitionblock note">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-note" title="Note"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>Every instance metadata can be accessed by the key available in the <a href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html">instance metadata service</a>
|
||
Nested properties can be accessed by separating the properties with a slash ('/').</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_using_instance_user_data">3.1.5. Using instance user data</h4>
|
||
<div class="paragraph">
|
||
<p>Besides the default instance metadata it is also possible to configure user data on each instance. This user data is retrieved and
|
||
parsed by Spring Cloud AWS. The user data can be defined while starting an EC2 instance with the application. Spring Cloud AWS
|
||
expects the format <code><key>:<value>;<key>:<value></code> inside the user data so that it can parse the string and extract the key value pairs.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The user data can be configured using either the management console shown below or a <a href="http://aws.amazon.com/cloudformation/aws-cloudformation-templates/">CloudFormation template</a>.</p>
|
||
</div>
|
||
<div class="imageblock">
|
||
<div class="content">
|
||
<img src="images/cloud-environment-user-data.png" alt="User data in the management console">
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>A CloudFormation template snippet for the configuration of the user data is outlined below:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-json" data-lang="json">...
|
||
"Resources": {
|
||
"ApplicationServerInstance": {
|
||
"Type": "AWS::EC2::Instance",
|
||
"Properties": {
|
||
"ImageId": "ami-6a56b81d",
|
||
"UserData": {
|
||
"Fn::Base64": "data1:value1;data2:value2"
|
||
},
|
||
"InstanceType": "t1.micro",
|
||
}
|
||
}
|
||
...</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The user data can be accessed directly in the application context like the instance metadata through placeholders
|
||
or expressions.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">@Component
|
||
public class SecondConfigurationBean {
|
||
|
||
@Value("${data1}")
|
||
private String firstDataOption;
|
||
|
||
@Value("${data2}")
|
||
private String secondDataOption;
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_using_instance_tags">3.1.6. Using instance tags</h4>
|
||
<div class="paragraph">
|
||
<p>User configured properties can also be configured with tags instead of user data. Tags are a global concept in the context
|
||
of Amazon Web services and used in different services. Spring Cloud AWS supports instance tags also across different
|
||
services. Compared to user data, user tags can be updated during runtime, there is no need to stop and restart
|
||
the instance.</p>
|
||
</div>
|
||
<div class="admonitionblock tip">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-tip" title="Tip"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p><a href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html">User data</a> can also be used to execute scripts
|
||
on instance startup. Therefore it is useful to leverage instance tags for user configuration and user data to execute scripts
|
||
on instance startup.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Instance specific tags can be configured on the instance level through the management console outlined below and
|
||
like user data also with a CloudFormation template shown afterwards.</p>
|
||
</div>
|
||
<div class="imageblock">
|
||
<div class="content">
|
||
<img src="images/cloud-environment-instance-tags.png" alt="Instance data in the management console">
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>A CloudFormation template snippet for the configuration of the instance tags is outlined below:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-json" data-lang="json">...
|
||
"Resources": {
|
||
"UserTagAndUserDataInstance": {
|
||
"Type": "AWS::EC2::Instance",
|
||
"Properties": {
|
||
"ImageId": "ami-6a56b81d",
|
||
"InstanceType": "t1.micro",
|
||
"Tags": [
|
||
{
|
||
"Key": "tag1",
|
||
"Value": "tagv1"
|
||
},
|
||
{
|
||
"Key": "tag3",
|
||
"Value": "tagv3"
|
||
},
|
||
{
|
||
"Key": "tag2",
|
||
"Value": "tagv2"
|
||
},
|
||
{
|
||
"Key": "tag4",
|
||
"Value": "tagv4"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
...</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>To retrieve the instance tags, Spring Cloud AWS has to make authenticated requests and therefore it will need the region
|
||
and security configuration before actually resolving the placeholders. Also because the instance tags are not available while starting
|
||
the application context, they can only be referenced as expressions and not with placeholders. The <code>context-instance-data</code>
|
||
element defines an attribute <code>user-tags-map</code> that will create a map in the application context for the name. This map
|
||
can then be queried using expression for other bean definitions.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans ...>
|
||
<aws-context:context-instance-data user-tags-map="instanceData" />
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>A java bean might resolve expressions with the <code>@Value</code> annotation.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">public class SimpleConfigurationBean {
|
||
|
||
@Value("#{instanceData.tag1}")
|
||
private String value1;
|
||
|
||
@Value("#{instanceData.tag2}")
|
||
private String value2;
|
||
|
||
@Value("#{instanceData.tag3}")
|
||
private String value3;
|
||
|
||
@Value("#{instanceData.tag4}")
|
||
private String value4;
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_configuring_custom_ec2_client">3.1.7. Configuring custom EC2 client</h4>
|
||
<div class="paragraph">
|
||
<p>In some circumstances it is necessary to have a custom EC2 client to retrieve the instance information. The
|
||
<code>context-instance-data</code> element supports a custom EC2 client with the <code>amazon-ec2</code> attribute. The next
|
||
example shows the use of a custom EC2 client that might have a special configuration in place.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans ...>
|
||
|
||
<aws-context:context-credentials>....</aws-context:context-credentials>
|
||
<aws-context:context-region ... />
|
||
<aws-context:context-instance-data amazon-ec2="myCustomClient"/>
|
||
|
||
<bean id="myCustomClient" class="com.amazonaws.services.ec2.AmazonEC2Client">
|
||
...
|
||
</bean>
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_injecting_the_default_ec2_client">3.1.8. Injecting the default EC2 client</h4>
|
||
<div class="paragraph">
|
||
<p>If there are user tags configured for the instance data (see above) Spring Cloud AWS configures an EC2 client with
|
||
the specified region and security credentials. Application developers can inject the EC2 client directly into their code using the <code>@Autowired</code> annotation.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">public class ApplicationService {
|
||
|
||
private final AmazonEC2 amazonEc2;
|
||
|
||
@Autowired
|
||
public ApplicationService(AmazonEC2 amazonEc2) {
|
||
this.amazonEc2 = amazonEc2;
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_managing_cloud_environments">4. Managing cloud environments</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>Managing environments manually with the management console does not scale and can become error-prone with the increasing
|
||
complexity of the infrastructure. Amazon Web services offers a <a href="http://aws.amazon.com/cloudformation/">CloudFormation</a>
|
||
service that allows to define stack configuration templates and bootstrap the whole infrastructure with the services.
|
||
In order to allow multiple stacks in parallel, each resource in the stack receives a unique physical name that contains
|
||
some arbitrary generated name. In order to interact with the stack resources in a unified way Spring Cloud AWS allows
|
||
developers to work with logical names instead of the random physical ones.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The next graphics shows a typical stack configuration.</p>
|
||
</div>
|
||
<div class="imageblock">
|
||
<div class="content">
|
||
<img src="images/cloudformation-overview.png" alt="CloudFormation overview">
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The <strong>Template File</strong> describes all stack resources with their <em>logical name</em>. The <strong>CloudFormation</strong> service parses the stack
|
||
template file and creates all resources with their <em>physical name</em>. The application can use all the stack configured resources
|
||
with the <em>logical name</em> defined in the template. Spring Cloud AWS resolves all <em>logical names</em> into the respective
|
||
<em>physical name</em> for the application developer.</p>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_automatic_cloudformation_configuration">4.1. Automatic CloudFormation configuration</h3>
|
||
<div class="paragraph">
|
||
<p>If the application runs inside a stack (because the underlying EC2 instance has been bootstrapped within the stack), then
|
||
Spring Cloud AWS will automatically detect the stack and resolve all resources from the stack. Application developers
|
||
can use all the logical names from the stack template to interact with the services. In the example below, the database
|
||
resource is configured using a CloudFormation template, defining a logical name for the database instance.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml">"applicationDatabase": {
|
||
"Type": "AWS::RDS::DBInstance",
|
||
"Properties": {
|
||
"AllocatedStorage": "5",
|
||
"DBInstanceClass": "db.t1.micro",
|
||
"DBName": "test"
|
||
...
|
||
]
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The datasource is then created and will receive a physical name (e.g. ir142c39k6o5irj) as the database service name. Application
|
||
developers can still use the logical name (in this case <code>applicationDatabase</code>) to interact with the database. The example
|
||
below shows the stack configuration which is defined by the element <code>aws-context:stack-configuration</code> and resolves automatically
|
||
the particular stack. The <code>data-source</code> element uses the logical name for the <code>db-instance-identifier</code> attribute to work with
|
||
the database.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
xmlns:aws-context="http://www.springframework.org/schema/cloud/aws/context"
|
||
xmlns="http://www.springframework.org/schema/beans"
|
||
xsi:schemaLocation="http://www.springframework.org/schema/cloud/aws/context
|
||
http://www.springframework.org/schema/cloud/aws/context/spring-cloud-aws-context-1.0.xsd">
|
||
|
||
<aws-context:context-credentials>
|
||
...
|
||
</aws-context:context-credentials>
|
||
|
||
<aws-context:context-region .. />
|
||
|
||
<aws-context:stack-configuration/>
|
||
|
||
<jdbc:data-source db-instance-identifier="applicationDatabase" ... />
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="admonitionblock tip">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-tip" title="Tip"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>Further detailed information on the Amazon RDS configuration and setup can be found in the respective chapter in this
|
||
documentation.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_manual_cloudformation_configuration">4.2. Manual CloudFormation configuration</h3>
|
||
<div class="paragraph">
|
||
<p>If the application is not running inside a stack configured EC2 instance, then the stack configuration must be configured
|
||
manually. The configuration consists of an additional element attribute <code>stack-name</code> that will be used to resolve all the
|
||
respective stack configuration information at runtime.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans ....>
|
||
...
|
||
<aws-context:stack-configuration stack-name="myStackName" />
|
||
...
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_cloudformation_configuration_with_java_config_classes">4.3. CloudFormation configuration with Java config classes</h3>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS also supports the configuration of the CloudFormation support within Java classes avoiding the use of
|
||
XML inside the application configuration. Spring Cloud AWS provides the annotation
|
||
<code>og.springframework.cloud.aws.context.config.annotation.EnableStackConfiguration</code> that allows the automatic and manual
|
||
stack configuration. The next example shows a configuration class that configures the CloudFormation support with an
|
||
explicit stack name (here <code>manualStackName</code>).</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">@Configuration
|
||
@EnableStackConfiguration(stackName = "manualStackName")
|
||
class ApplicationConfiguration {
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="admonitionblock tip">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-tip" title="Tip"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>Do not define the <code>stackName</code> attribute if an automatic stack name should be enabled.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_cloudformation_configuration_in_spring_boot">4.4. CloudFormation configuration in Spring Boot</h3>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS also supports the configuration of the CloudFormation support within the Spring Boot configuration. The
|
||
manual and automatic stack configuration can be defined with properties that are described in the table below.</p>
|
||
</div>
|
||
<table class="tableblock frame-all grid-all spread">
|
||
<colgroup>
|
||
<col style="width: 33%;">
|
||
<col style="width: 33%;">
|
||
<col style="width: 33%;">
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th class="tableblock halign-left valign-top">property</th>
|
||
<th class="tableblock halign-left valign-top">example</th>
|
||
<th class="tableblock halign-left valign-top">description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">cloud.aws.stack.name</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">myStackName</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">The name of the manually configured stack name that will be used to retrieve the resources.</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">cloud.aws.stack.auto</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Enables the automatic stack name detection for the application.</p></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_manual_name_resolution">4.5. Manual name resolution</h3>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS uses the CloudFormation stack to resolve all resources internally using the logical names. In some circumstances
|
||
it might be needed to resolve the physical name inside the application code. Spring Cloud AWS provides a pre-configured
|
||
service to resolve the physical stack name based on the logical name. The sample shows a manual stack resource resolution.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">@Service
|
||
public class ApplicationService {
|
||
|
||
private final ResourceIdResolver resourceIdResolver;
|
||
|
||
@Autowired
|
||
public ApplicationService(ResourceIdResolver resourceIdResolver) {
|
||
this.resourceIdResolver = resourceIdResolver;
|
||
}
|
||
|
||
public void handleApplicationLogic() {
|
||
String physicalBucketName =
|
||
this.resourceIdResolver.resolveToPhysicalResourceId("someLogicalName");
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_stack_tags">4.6. Stack Tags</h3>
|
||
<div class="paragraph">
|
||
<p>Like for the Amazon EC2 instances, CloudFormation also provides stack specific tags that can be used to
|
||
configure stack specific configuration information and receive them inside the application. This can for example be a
|
||
stage specific configuration property (like DEV, INT, PRD).</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans ....>
|
||
...
|
||
<aws-context:stack-configuration user-tags-map="stackTags"/>
|
||
...
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The application can then access the stack tags with an expression like <code>#{stackTags.key1}</code>.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_using_custom_cloudformation_client">4.7. Using custom CloudFormation client</h3>
|
||
<div class="paragraph">
|
||
<p>Like for the EC2 configuration setup, the <code>aws-context:stack-configuration</code> element supports a custom CloudFormation client
|
||
with a special setup. The client itself can be configured using the <code>amazon-cloud-formation</code> attribute as shown in the example:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans>
|
||
<aws-context:stack-configuration amazon-cloud-formation=""/>
|
||
|
||
<bean class="com.amazonaws.services.cloudformation.AmazonCloudFormationClient">
|
||
</bean>
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_messaging">5. Messaging</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS provides <a href="http://aws.amazon.com/sqs/">Amazon SQS</a> and <a href="http://aws.amazon.com/sqs/">Amazon SNS</a> integration
|
||
that simplifies the publication and consumption of messages over SQS or SNS. While SQS fully relies on the messaging API
|
||
introduced with Spring 4.0, SNS only partially implements it as the receiving part must be handled differently for
|
||
push notifications.</p>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_configuring_messaging">5.1. Configuring messaging</h3>
|
||
<div class="paragraph">
|
||
<p>Before using and configuring the messaging support, the application has to include the respective module dependency
|
||
into the Maven configuration. Spring Cloud AWS Messaging support comes as a separate module to allow the modularized use
|
||
of the modules.</p>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_maven_dependency_configuration">5.1.1. Maven dependency configuration</h4>
|
||
<div class="paragraph">
|
||
<p>The Spring Cloud AWS messaging module comes as a standalone module and can be imported with the following dependency declaration:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><dependency>
|
||
<groupId>org.springframework.cloud</groupId>
|
||
<artifactId>spring-cloud-aws-messaging</artifactId>
|
||
<version>{spring-cloud-version}</version>
|
||
</dependency></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_sqs_support">5.2. SQS support</h3>
|
||
<div class="paragraph">
|
||
<p>Amazon SQS is a hosted messaging service on the Amazon Web Service platform that provides point-to-point communication
|
||
with queues. Compared to JMS or other message services Amazon SQS has several features and limitations that should be
|
||
taken into consideration.</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p>Amazon SQS allows only <code>String</code> payloads, so any <code>Object</code> must be transformed into a String representation.
|
||
Spring Cloud AWS has dedicated support to transfer Java objects with Amazon SQS messages by converting them to JSON.</p>
|
||
</li>
|
||
<li>
|
||
<p>Amazon SQS has no transaction support, so messages might therefore be retrieved twice. Application have to be written in
|
||
an idempotent way so that they can receive a message twice.</p>
|
||
</li>
|
||
<li>
|
||
<p>Amazon SQS has a maximum message size of 256kb per message, so bigger messages will fail to be sent.</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_sending_a_message">5.2.1. Sending a message</h4>
|
||
<div class="paragraph">
|
||
<p>The <code>QueueMessagingTemplate</code> contains many convenience methods to send a message. There are send methods that specify the
|
||
destination using a <code>QueueMessageChannel</code> object and those that specify the destination using a string which is going to
|
||
be resolved against the SQS API. The send method that takes no destination argument uses the default destination.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">import com.amazonaws.services.sqs.AmazonSQS;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate;
|
||
import org.springframework.messaging.support.MessageBuilder;
|
||
|
||
public class SqsQueueSender {
|
||
|
||
private final QueueMessagingTemplate queueMessagingTemplate;
|
||
|
||
@Autowired
|
||
public SqsQueueSender(AmazonSQS amazonSqs) {
|
||
this.queueMessagingTemplate = new QueueMessagingTemplate(amazonSqs);
|
||
}
|
||
|
||
public void send(String message) {
|
||
this.queueMessagingTemplate.send("physicalQueueName", MessageBuilder.withPayload(message).build());
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>This example uses the <code>MessageBuilder</code> class to create a message with a string payload. The <code>QueueMessagingTemplate</code> is
|
||
constructed by passing a reference to the <code>AmazonSQS</code> client. The destination in the send method is a string value that
|
||
must match the queue name defined on AWS. This value will be resolved at runtime by the Amazon SQS client. Optionally
|
||
a <code>ResourceIdResolver</code> implementation can be passed to the <code>QueueMessagingTemplate</code> constructor to resolve resources by
|
||
logical name when running inside a CloudFormation stack (see <a href="#_managing_cloud_environments">Managing cloud environments</a> for more information about
|
||
resource name resolution).</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>With the messaging namespace a <code>QueueMessagingTemplate</code> can be defined in an XML configuration file.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
xmlns:aws-context="http://www.springframework.org/schema/cloud/aws/context"
|
||
xmlns:aws-messaging="http://www.springframework.org/schema/cloud/aws/messaging"
|
||
xmlns="http://www.springframework.org/schema/beans"
|
||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||
http://www.springframework.org/schema/cloud/aws/context
|
||
http://www.springframework.org/schema/cloud/aws/context/spring-cloud-aws-context-1.0.xsd
|
||
http://www.springframework.org/schema/cloud/aws/messaging
|
||
http://www.springframework.org/schema/cloud/aws/messaging/spring-cloud-aws-messaging-1.0.xsd">
|
||
|
||
<aws-context:context-credentials>
|
||
<aws-context:instance-profile-credentials />
|
||
</aws-context:context-credentials>
|
||
|
||
<aws-messaging:queue-messaging-template id="queueMessagingTemplate" />
|
||
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In this example the messaging namespace handler constructs a new <code>QueueMessagingTemplate</code>. The <code>AmazonSQS</code> client
|
||
is automatically created and passed to the template’s constructor based on the provided credentials. If the
|
||
application runs inside a configured CloudFormation stack a <code>ResourceIdResolver</code> is passed to the constructor (see
|
||
<a href="#_managing_cloud_environments">Managing cloud environments</a> for more information about resource name resolution).</p>
|
||
</div>
|
||
<div class="sect4">
|
||
<h5 id="_using_message_converters">Using message converters</h5>
|
||
<div class="paragraph">
|
||
<p>In order to facilitate the sending of domain model objects, the <code>QueueMessagingTemplate</code> has various send methods that
|
||
take a Java object as an argument for a message’s data content. The overloaded methods <code>convertAndSend()</code> and
|
||
<code>receiveAndConvert()</code> in <code>QueueMessagingTemplate</code> delegate the conversion process to an instance of the <code>MessageConverter</code>
|
||
interface. This interface defines a simple contract to convert between Java objects and SQS messages. The default
|
||
implementation <code>SimpleMessageConverter</code> simply unwraps the message payload as long as it matches the target type. By
|
||
using the converter, you and your application code can focus on the business object that is being sent or received via
|
||
SQS and not be concerned with the details of how it is represented as an SQS message.</p>
|
||
</div>
|
||
<div class="admonitionblock note">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-note" title="Note"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>As SQS is only able to send <code>String</code> payloads the default converter <code>SimpleMessageConverter</code> should only be used
|
||
to send <code>String</code> payloads. For more complex objects a custom converter should be used like the one created by the
|
||
messaging namespace handler.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>It is recommended to use the XML messaging namespace to create <code>QueueMessagingTemplate</code> as it will set a more
|
||
sophisticated <code>MessageConverter</code> that converts objects into JSON when Jackson is on the classpath.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><aws-messaging:queue-messaging-template id="queueMessagingTemplate" /></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">this.queueMessagingTemplate.convertAndSend("queueName", new Person("John, "Doe"));</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In this example a <code>QueueMessagingTemplate</code> is created using the messaging namespace. The <code>convertAndSend</code> method
|
||
converts the payload <code>Person</code> using the configured <code>MessageConverter</code> and sends the message.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_receiving_a_message">5.2.2. Receiving a message</h4>
|
||
<div class="paragraph">
|
||
<p>There are two ways for receiving SQS messages, either use the <code>receive</code> methods of the <code>QueueMessagingTemplate</code> or with
|
||
annotation-driven listener endpoints. The latter is by far the more convenient way to receive messages.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">Person person = this.queueMessagingTemplate.receiveAndConvert("queueName", Person.class);</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In this example the <code>QueueMessagingTemplate</code> will get one message from the SQS queue and convert it to the target class
|
||
passed as argument.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_annotation_driven_listener_endpoints">5.2.3. Annotation-driven listener endpoints</h4>
|
||
<div class="paragraph">
|
||
<p>Annotation-driven listener endpoints are the easiest way for listening on SQS messages. Simply annotate methods with
|
||
<code>MessageMapping</code> and the <code>QueueMessageHandler</code> will route the messages to the annotated methods.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><aws-messaging:annotation-driven-queue-listener /></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">@MessageMapping("queueName")
|
||
public void queueListener(Person person) {
|
||
// ...
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In this example a queue listener container is started that polls the SQS <code>queueName</code> passed to the <code>MessageMapping</code>
|
||
annotation. The incoming messages are converted to the target type and then the annotated method <code>queueListener</code> is invoked.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In addition to the payload, headers can be injected in the listener methods with the <code>@Header</code> or <code>@Headers</code>
|
||
annotations. <code>@Header</code> is used to inject a specific header value while <code>@Headers</code> injects a <code>Map<String, String></code>
|
||
containing all headers.</p>
|
||
</div>
|
||
<div class="admonitionblock note">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-note" title="Note"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>It is recommended to use the XML messaging namespace to create <code>QueueMessagingTemplate</code> as it will set a more
|
||
sophisticated <code>MessageConverter</code> that converts objects into JSON when Jackson is on the classpath.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><aws-messaging:queue-messaging-template id="queueMessagingTemplate" /></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">this.queueMessagingTemplate.convertAndSend("queueName", new Person("John, "Doe"));</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In this example a <code>QueueMessagingTemplate</code> is created using the messaging namespace. The <code>convertAndSend</code> method
|
||
converts the payload <code>Person</code> using the configured <code>MessageConverter</code> and sends the message.
|
||
Only the <a href="http://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_Message.html">standard
|
||
message attributes</a> sent with an SQS message are supported. Custom attributes are currently not supported.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In addition to the provided argument resolvers, custom ones can be registered on the
|
||
<code>aws-messaging:annotation-driven-queue-listener</code> element using the <code>aws-messaging:argument-resolvers</code> attribute (see example below).</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><aws-messaging:annotation-driven-queue-listener>
|
||
<aws-messaging:argument-resolvers>
|
||
<bean class="org.custom.CustomArgumentResolver" />
|
||
</aws-messaging:argument-resolvers>
|
||
</aws-messaging:annotation-driven-queue-listener></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>By default the <code>SimpleMessageListenerContainer</code> creates a <code>ThreadPoolTaskExecutor</code> with computed values for the core and
|
||
max pool sizes. The core pool size is set to twice the number of queues and the max pool size is obtained by multiplying
|
||
the number of queues by the value of the <code>maxNumberOfMessages</code> field. If these default values do not meet the need of
|
||
the application, a custom task executor can be set with the <code>task-executor</code> attribute (see example below).</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><aws-messaging:annotation-driven-queue-listener task-executor="simpleTaskExecutor" /></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="sect4">
|
||
<h5 id="_message_reply">Message reply</h5>
|
||
<div class="paragraph">
|
||
<p>Message listener methods can be annotated with <code>@SendTo</code> to send their return value to another channel. The
|
||
<code>SendToHandlerMethodReturnValueHandler</code> uses the defined messaging template set on the
|
||
<code>aws-messaging:annotation-driven-queue-listener</code> element to send the return value. The messaging template must implement
|
||
the <code>DestinationResolvingMessageSendingOperations</code> interface.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><aws-messaging:annotation-driven-queue-listener send-to-message-template="queueMessagingTemplate"/></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">@MessageMapping("treeQueue")
|
||
@SendTo("leafsQueue")
|
||
public List<Leaf> extractLeafs(Tree tree) {
|
||
// ...
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In this example the <code>extractLeafs</code> method will receive messages coming from the <code>treeQueue</code> and then return a
|
||
<code>List</code> of <code>Leaf</code>s which is going to be sent to the <code>leafsQueue</code>. Note that on the
|
||
<code>aws-messaging:annotation-driven-queue-listener</code> XML element there is an attribute <code>send-to-message-template</code>
|
||
that specifies <code>QueueMessagingTemplate</code> as the messaging template to be used to send the return value of the message
|
||
listener method.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_the_simplemessagelistenercontainerfactory">5.2.4. The SimpleMessageListenerContainerFactory</h4>
|
||
<div class="paragraph">
|
||
<p>The <code>SimpleMessageListenerContainer</code> can also be configured with Java by creating a bean of type <code>SimpleMessageListenerContainerFactory</code>.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">@Bean
|
||
public SimpleMessageListenerContainerFactory simpleMessageListenerContainerFactory(AmazonSQSAsync amazonSqs) {
|
||
SimpleMessageListenerContainerFactory factory = new SimpleMessageListenerContainerFactory();
|
||
factory.setAmazonSqs(amazonSqs);
|
||
factory.setAutoStartup(false);
|
||
factory.setMaxNumberOfMessages(5);
|
||
// ...
|
||
|
||
return factory;
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_sns_support">5.3. SNS support</h3>
|
||
<div class="paragraph">
|
||
<p>Amazon SNS is a publish-subscribe messaging system that allows clients to publish notification to a particular topic. Other
|
||
interested clients may subscribe using different protocols like HTTP/HTTPS, e-mail or an Amazon SQS queue to receive the messages.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The next graphic shows a typical example of an Amazon SNS architecture.</p>
|
||
</div>
|
||
<div class="imageblock">
|
||
<div class="content">
|
||
<img src="images/sns-overview.png" alt="SNS Overview">
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS supports Amazon SNS by providing support to send notifications with a <code>NotificationMessagingTemplate</code> and
|
||
to receive notifications with the HTTP/HTTPS endpoint using the Spring Web MVC <code>@Controller</code> based programming model. Amazon
|
||
SQS based subscriptions can be used with the annotation-driven message support that is provided by the Spring Cloud AWS messaging module.</p>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_sending_a_message_2">5.3.1. Sending a message</h4>
|
||
<div class="paragraph">
|
||
<p>The <code>NotificationMessagingTemplate</code> contains two convenience methods to send a notification. The first one specifies the
|
||
destination using a <code>String</code> which is going to be resolved against the SNS API. The second one takes no destination
|
||
argument and uses the default destination. All the usual send methods that are available on the <code>MessageSendingOperations</code>
|
||
are implemented but are less convenient to send notifications because the subject must be passed as header.</p>
|
||
</div>
|
||
<div class="admonitionblock note">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-note" title="Note"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>Currently only <code>String</code> payloads can be sent using the <code>NotificationMessagingTemplate</code> as this is the expected
|
||
type by the SNS API.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">import com.amazonaws.services.sns.AmazonSNS;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import org.springframework.cloud.aws.messaging.core.NotificationMessagingTemplate;
|
||
|
||
public class SnsNotificationSender {
|
||
|
||
private final NotificationMessagingTemplate notificationMessagingTemplate;
|
||
|
||
@Autowired
|
||
public SnsNotificationSender(AmazonSNS amazonSns) {
|
||
this.notificationMessagingTemplate = new NotificationMessagingTemplate(amazonSns);
|
||
}
|
||
|
||
public void send(String subject, String message) {
|
||
this.notificationMessagingTemplate.sendNotification("physicalTopicName", message, subject);
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>This example constructs a new <code>NotificationMessagingTemplate</code> by passing an <code>AmazonSNS</code> client as argument. In the <code>send</code>
|
||
method the convenience <code>sendNotification</code> method is used to send a <code>message</code> with <code>subject</code> to an SNS topic. The
|
||
destination in the <code>sendNotification</code> method is a string value that must match the topic name defined on AWS. This value
|
||
is resolved at runtime by the Amazon SNS client. Optionally a <code>ResourceIdResolver</code> implementation can be passed to the
|
||
<code>NotificationMessagingTemplate</code> constructor to resolve resources by logical name when running inside a CloudFormation stack.
|
||
(See <a href="#_managing_cloud_environments">Managing cloud environments</a> for more information about resource name resolution.)</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>It is recommended to use the XML messaging namespace to create <code>NotificationMessagingTemplate</code> as it will automatically
|
||
configure the SNS client to setup the default converter.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><aws-messaging:notification-messaging-template id="notificationMessagingTemplate" /></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_annotation_driven_http_notification_endpoint">5.3.2. Annotation-driven HTTP notification endpoint</h4>
|
||
<div class="paragraph">
|
||
<p>SNS supports multiple endpoint types (SQS, Email, HTTP, HTTPS), Spring Cloud AWS provides support for HTTP(S) endpoints.
|
||
SNS sends three type of requests to an HTTP topic listener endpoint, for each of them annotations are provided:</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p>Subscription request → <code>@NotificationSubscriptionMapping</code></p>
|
||
</li>
|
||
<li>
|
||
<p>Notification request → <code>@NotificationMessageMapping</code></p>
|
||
</li>
|
||
<li>
|
||
<p>Unsubscription request → <code>@NotificationUnsubscribeMapping</code></p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>HTTP endpoints are based on Spring MVC controllers. Spring Cloud AWS added some custom argument resolvers to extract
|
||
the message and subject out of the notification requests.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">@Controller
|
||
@RequestMapping("/topicName")
|
||
public class NotificationTestController {
|
||
|
||
@NotificationSubscriptionMapping
|
||
public void handleSubscriptionMessage(NotificationStatus status) throws IOException {
|
||
//We subscribe to start receive the message
|
||
status.confirmSubscription();
|
||
}
|
||
|
||
@NotificationMessageMapping
|
||
public void handleNotificationMessage(@NotificationSubject String subject, @NotificationMessage String message) {
|
||
// ...
|
||
}
|
||
|
||
@NotificationUnsubscribeConfirmationMapping
|
||
public void handleUnsubscribeMessage(NotificationStatus status) {
|
||
//e.g. the client has been unsubscribed and we want to "re-subscribe"
|
||
status.confirmSubscription();
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="admonitionblock caution">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-caution" title="Caution"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>Currently it is not possible to define the mapping URL on the method level therefore the <code>RequestMapping</code> must
|
||
be done at type level and must contain the full path of the endpoint.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>This example creates a new Spring MVC controller with three methods to handle the three requests listed above. In order
|
||
to resolve the arguments of the <code>handleNotificationMessage</code> methods a custom argument resolver must be registered. The
|
||
XML configuration is listed below.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><mvc:annotation-driven>
|
||
<mvc:argument-resolvers>
|
||
<ref bean="notificationResolver" />
|
||
</mvc:argument-resolvers>
|
||
</mvc:annotation-driven>
|
||
|
||
<aws-messaging:notification-argument-resolver id="notificationResolver" /></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The <code>aws-messaging:notification-argument-resolver</code> element registers three argument resolvers:
|
||
<code>NotificationStatusHandlerMethodArgumentResolver</code>, <code>NotificationMessageHandlerMethodArgumentResolver</code>,
|
||
and <code>NotificationSubjectHandlerMethodArgumentResolver</code>.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_using_cloudformation">5.4. Using CloudFormation</h3>
|
||
<div class="paragraph">
|
||
<p>Amazon SQS queues and SNS topics can be configured within a stack and then be used by applications. Spring Cloud AWS
|
||
also supports the lookup of stack-configured queues and topics by their logical name with the resolution to the physical
|
||
name. The example below shows an SNS topic and SQS queue configuration inside a CloudFormation template.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-json" data-lang="json">"LogicalQueueName": {
|
||
"Type": "AWS::SQS::Queue",
|
||
"Properties": {
|
||
}
|
||
},
|
||
"LogicalTopicName": {
|
||
"Type": "AWS::SNS::Topic",
|
||
"Properties": {
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The logical names <code>LogicalQueueName</code> and <code>LogicalTopicName</code> can then be used in the configuration and in the application
|
||
as shown below:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><aws-messaging:queue-messaging-template default-destination="LogicalQueueName" />
|
||
|
||
<aws-messaging:notification-messaging-template default-destination="LogicalTopicName" /></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">@MessageMapping("LogicalQueueName")
|
||
public void receiveQueueMessages(Person person) {
|
||
// Logical names can also be used with messaging templates
|
||
this.notificationMessagingTemplate.sendNotification("anotherLogicalTopicName", "Message", "Subject");
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>When using the logical names like in the example above, the stack can be created on different environments without any
|
||
configuration or code changes inside the application.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_caching">6. Caching</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>Caching in a cloud environment is useful for applications to reduce the latency and to save database round trips.
|
||
Reducing database round trips can significantly reduce the requirements for the database instance. The Spring Framework
|
||
provides, since version 3.1, a unified Cache abstraction to allow declarative caching in applications analogous to the
|
||
declarative transactions.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS integrates the <a href="http://aws.amazon.com/elasticache/">Amazon ElastiCache</a> service into the Spring unified
|
||
caching abstraction providing a cache manager based on the memcached and Redis protocols. The caching support for Spring
|
||
Cloud AWS provides its own memcached implementation for ElastiCache and uses
|
||
<a href="http://projects.spring.io/spring-data-redis/">Spring Data Redis</a> for Redis caches.</p>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_configuring_dependencies_for_redis_caches">6.1. Configuring dependencies for Redis caches</h3>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS delivers its own implementation of a memcached cache, therefore no other dependencies are needed. For Redis
|
||
Spring Cloud AWS relies on Spring Data Redis to support caching and also to allow multiple Redis drivers to be used. Spring
|
||
Cloud AWS supports all Redis drivers that Spring Data Redis supports (currently Jedis, JRedis, SRP and Lettuce) with Jedis
|
||
being used internally for testing against ElastiCache. A dependency definition for Redis with Jedis is shown in the example</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><dependencies>
|
||
<dependency>
|
||
<groupId>org.springframework.data</groupId>
|
||
<artifactId>spring-data-redis</artifactId>
|
||
<version>${spring-data-redis.version}</version>
|
||
</dependency>
|
||
<dependency>
|
||
<groupId>redis.clients</groupId>
|
||
<artifactId>jedis</artifactId>
|
||
<version>2.6.1</version>
|
||
</dependency>
|
||
</dependencies></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS will automatically detect the Redis driver and will use one of them automatically.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_configuring_caching_with_xml">6.2. Configuring caching with XML</h3>
|
||
<div class="paragraph">
|
||
<p>The cache support for Spring Cloud AWS resides in the context module and can therefore be used if the context module
|
||
is already imported in the project. The cache integration provides its own namespace to configure cache clusters that are
|
||
hosted in the Amazon ElastiCache service. The next example contains a configuration for the cache cluster and the Spring
|
||
configuration to enable declarative, annotation-based caching.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans xmlns:aws-cache="http://www.springframework.org/schema/cloud/aws/cache"
|
||
xmlns:cache="http://www.springframework.org/schema/cache"
|
||
xmlns="http://www.springframework.org/schema/beans"
|
||
xsi:schemaLocation="http://www.springframework.org/schema/cloud/aws/cache
|
||
http://www.springframework.org/schema/cloud/aws/cache/spring-cloud-aws-cache-1.0.xsd
|
||
http://www.springframework.org/schema/cache
|
||
http://www.springframework.org/schema/cache/spring-cache.xsd">
|
||
|
||
<aws-context:context-credentials>
|
||
...
|
||
</aws-context:context-credentials>
|
||
|
||
<aws-cache:cache-manager>
|
||
<aws-cache:cache-cluster name="CacheCluster" />
|
||
</aws-cache:cache-manager>
|
||
|
||
<cache:annotation-driven />
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The configuration above configures a <code>cache-manager</code> with one cache with the name <code>CacheCluster</code> that represents an
|
||
<a href="http://docs.aws.amazon.com/AmazonElastiCache/latest/UserGuide/ManagingCacheClusters.html">ElasticCache cluster</a>.</p>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_mixing_caches">6.2.1. Mixing caches</h4>
|
||
<div class="paragraph">
|
||
<p>Applications may have the need for multiple caches that are maintained by one central cache cluster. The Spring Cloud
|
||
AWS caching support allows to define multiple caches inside one cache manager and also to use externally defined caches
|
||
inside the cache manager.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The example below demonstrates a configuration example that contains a pre-configured cache with a <code>cache-ref</code> element
|
||
(which might be a local cache) and a <code>cache-cluster</code> configuration for ElastiCache cache clusters.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans ...>
|
||
<aws-cache:cache-manager id="cacheManager">
|
||
<aws-cache:cache-ref ref="memcached" />
|
||
<aws-cache:cache-cluster name="SimpleCache"/>
|
||
</aws-cache:cache-manager>
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_defining_expiration">6.2.2. Defining expiration</h4>
|
||
<div class="paragraph">
|
||
<p>The Spring cache demarcation does not support expiry time configuration and leaves it up to the cache implementation
|
||
to support an expiry time. The Spring Cloud AWS cache configuration supports the expiry time setting per cache. The
|
||
expiry time will be passed to the memcached service.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The <code>cache-cluster</code> element accepts an expiration attribute that defines the expiration time in seconds.
|
||
No configured values implies that there is an infinite expiration time.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans>
|
||
<aws-cache:cache-manager>
|
||
<aws-cache:cache-cluster expiration="10000" name="CacheCluster" />
|
||
</aws-cache:cache-manager>
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_configuring_caching_using_java_configuration">6.3. Configuring caching using Java configuration</h3>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS also support the cache configuration with Java configuration classes. On any <code>Configuration</code> class,
|
||
the caching can be configured using the <code>org.springframework.cloud.aws.cache.config.annotation.EnableElastiCache</code>
|
||
annotation provided by Spring Cloud AWS. The next example shows a configuration of two cache clusters.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">@EnableElastiCache({@CacheClusterConfig(name = "firstCache"), @CacheClusterConfig(name = "secondCache")})
|
||
public class ApplicationConfiguration {
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="admonitionblock important">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-important" title="Important"></i>
|
||
</td>
|
||
<td class="content">
|
||
If you leave the <code>value</code> attribute empty, then all the caches inside your CloudFormation stack (if available)
|
||
will be configured automatically.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_configuring_expiry_time_for_caches">6.3.1. Configuring expiry time for caches</h4>
|
||
<div class="paragraph">
|
||
<p>The Java configuration also allows to configure the expiry time for the caches. This can be done for all
|
||
caches using the <code>defaultExpiration</code> attribute as shown in the example below.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">@EnableElastiCache(defaultExpiration = 23)
|
||
public class ApplicationConfiguration {
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The expiration can be defined on a cache level using the <code>@CacheClusterConfig</code> annotations expiration attribute as shown below (using seconds as
|
||
the value).</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">@EnableElastiCache({@CacheClusterConfig(name = "firstCache", expiration = 23), @CacheClusterConfig(name = "secondCache", expiration = 42)})
|
||
public class ApplicationConfiguration {
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_configuring_caching_in_spring_boot">6.4. Configuring caching in Spring Boot</h3>
|
||
<div class="paragraph">
|
||
<p>The caches will automatically be configured in Spring Boot without any explicit configuration property.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_using_caching">6.5. Using caching</h3>
|
||
<div class="paragraph">
|
||
<p>Based on the configuration of the cache, developers can annotate their methods to use the caching for method return values.
|
||
The next example contains a caching declaration for a service for which the return values should be cached</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">@Service
|
||
public class ExpensiveService {
|
||
|
||
@Cacheable("CacheCluster")
|
||
public String calculateExpensiveValue(String key) {
|
||
...
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_memcached_client_implementation">6.6. Memcached client implementation</h3>
|
||
<div class="paragraph">
|
||
<p>There are different memcached client implementations available for Java, the most prominent ones are
|
||
<a href="https://github.com/couchbase/spymemcached">Spymemcached</a> and <a href="https://github.com/killme2008/xmemcached">XMemcached</a>.
|
||
Amazon AWS supports a dynamic configuration and delivers an enhanced memcached client based on Spymemcached to support the
|
||
<a href="http://docs.aws.amazon.com/AmazonElastiCache/latest/UserGuide/AutoDiscovery.html">auto-discovery</a> of new nodes based on
|
||
a central configuration endpoint.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS relies on the Amazon ElastiCache Client implementation and therefore has a dependency on that.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_using_cloudformation_2">6.7. Using CloudFormation</h3>
|
||
<div class="paragraph">
|
||
<p>Amazon ElastiCache clusters can also be configured within a stack and then be used by applications. Spring Cloud AWS
|
||
also supports the lookup of stack-configured cache clusters by their logical name with the resolution to the physical
|
||
name. The example below shows a cache cluster configuration inside a CloudFormation template.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-json" data-lang="json">"CacheCluster": {
|
||
"Type": "AWS::ElastiCache::CacheCluster",
|
||
"Properties": {
|
||
"AutoMinorVersionUpgrade": "true",
|
||
"Engine": "memcached",
|
||
"CacheNodeType": "cache.t2.micro",
|
||
"CacheSubnetGroupName" : "sample",
|
||
"NumCacheNodes": "1",
|
||
"VpcSecurityGroupIds": ["sample1"]
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The cache cluster can then be used with the name <code>CacheCluster</code> inside the application configuration as shown below:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans...>
|
||
<aws-cache:cache-manager>
|
||
<aws-cache:cache-cluster name="CacheCluster" expiration="15"/>
|
||
</aws-cache:cache-manager>
|
||
<beans></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>With the configuration above the application can be deployed with multiple stacks on different environments
|
||
without any configuration change inside the application.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_data_access_with_jdbc">7. Data Access with JDBC</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>Spring has a broad support of data access technologies built on top of JDBC like <code>JdbcTemplate</code> and dedicated ORM (JPA,
|
||
Hibernate support). Spring Cloud AWS enables application developers to re-use their JDBC technology of choice and access the
|
||
<a href="http://aws.amazon.com/rds/">Amazon Relational Database Service</a> with a declarative configuration. The main support provided by Spring
|
||
Cloud AWS for JDBC data access are:</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p>Automatic data source configuration and setup based on the Amazon RDS database instance.</p>
|
||
</li>
|
||
<li>
|
||
<p>Automatic read-replica detection and configuration for Amazon RDS database instances.</p>
|
||
</li>
|
||
<li>
|
||
<p>Retry-support to handle exception during Multi-AZ failover inside the data center.</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_configuring_data_source">7.1. Configuring data source</h3>
|
||
<div class="paragraph">
|
||
<p>Before using and configuring the database support, the application has to include the respective module dependency
|
||
into its Maven configuration. Spring Cloud AWS JDBC support comes as a separate module to allow the modularized use of the
|
||
modules.</p>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_maven_dependency_configuration_2">7.1.1. Maven dependency configuration</h4>
|
||
<div class="paragraph">
|
||
<p>The Spring Cloud AWS JDBC module comes as a standalone module and can be imported with the following dependency declaration.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><dependency>
|
||
<groupId>org.springframework.cloud</groupId>
|
||
<artifactId>spring-cloud-aws-jdbc</artifactId>
|
||
<version>{spring-cloud-version}</version>
|
||
</dependency></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_basic_data_source_configuration">7.1.2. Basic data source configuration</h4>
|
||
<div class="paragraph">
|
||
<p>The data source configuration requires the security and region configuration as a minimum allowing Spring Cloud AWS to retrieve
|
||
the database metadata information with the Amazon RDS service. Spring Cloud AWS provides an additional <code>jdbc</code> specific namespace
|
||
to configure the data source with the minimum attributes as shown in the example:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
xmlns:jdbc="http://www.springframework.org/schema/cloud/aws/jdbc"
|
||
xmlns="http://www.springframework.org/schema/beans"
|
||
xsi:schemaLocation="http://www.springframework.org/schema/cloud/aws/jdbc
|
||
http://www.springframework.org/schema/cloud/aws/jdbc/spring-cloud-aws-jdbc-1.0.xsd">
|
||
|
||
<aws-context:context-credentials>
|
||
...
|
||
</aws-context:context-credentials>
|
||
|
||
<aws-context:context-region region="..."/>
|
||
|
||
<jdbc:data-source
|
||
db-instance-identifier="myRdsDatabase"
|
||
password="${rdsPassword}">
|
||
</jdbc:data-source>
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The minimum configuration parameters are a unique <code>id</code> for the data source, a valid <code>db-instance-identifier</code> attribute
|
||
that points to a valid Amazon RDS database instance. The master user password for the master user. If there is another
|
||
user to be used (which is recommended) then the <code>username</code> attribute can be set.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>With this configuration Spring Cloud AWS fetches all the necessary metadata and creates a
|
||
<a href="http://tomcat.apache.org/tomcat-7.0-doc/jdbc-pool.html">Tomcat JDBC pool</a> with the default properties. The data source
|
||
can be later injected into any Spring Bean as shown below:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">@Service
|
||
public class SimpleDatabaseService implements DatabaseService {
|
||
|
||
private final JdbcTemplate jdbcTemplate;
|
||
|
||
@Autowired
|
||
public SimpleDatabaseService(DataSource dataSource) {
|
||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>It is possible to qualify the data source injection point with an <code>@Qualifier</code> annotation to allow multiple data source
|
||
configurations inside one application context and still use auto-wiring.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_data_source_pool_configuration">7.1.3. Data source pool configuration</h4>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS creates a new Tomcat JDBC pool with the default properties. Often these default properties do not meet the
|
||
requirements of the application with regards to pool size and other settings. The data source configuration supports the configuration
|
||
of all valid pool properties with a nested XML element. The following example demonstrates the re-configuration of the data source
|
||
with custom pool properties.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans ..>
|
||
|
||
<aws-context:context-credentials>
|
||
...
|
||
</aws-context:context-credentials>
|
||
|
||
<aws-context:context-region region="..."/>
|
||
|
||
<jdbc:data-source
|
||
db-instance-identifier="myRdsDatabase"
|
||
password="${rdsPassword}">
|
||
<jdbc:pool-attributes initialSize="1" " maxActive="200" minIdle="10"
|
||
testOnBorrow="true" validationQuery="SELECT 1" />
|
||
</jdbc:data-source>
|
||
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>A full list of all configuration attributes with their value is available <a href="http://tomcat.apache.org/tomcat-7.0-doc/jdbc-pool.html">here</a>.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_configuring_data_source_with_java_config">7.2. Configuring data source with Java config</h3>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS also supports the configuration of the data source within an <code>@Configuration</code> class. The
|
||
<code>org.springframework.cloud.aws.jdbc.config.annotation.EnableRdsInstance</code> annotation can be used to configure one data
|
||
source. Multiple ones can be used to configure more then one data source. Each annotation will generate exactly one
|
||
data source bean.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The class below shows a data source configuration inside a configuration class</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">@Configuration
|
||
@EnableRdsInstance(dbInstanceIdentifier = "test",password = "secret", readReplicaSupport = true)
|
||
public class ApplicationConfiguration {
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="admonitionblock tip">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-tip" title="Tip"></i>
|
||
</td>
|
||
<td class="content">
|
||
The configuration attributes are the same in the XML element. The required attributes are also the same
|
||
for the XML configuration (the <code>dbInstanceIdentifier</code> and <code>password</code> attribute)
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_java_based_data_source_pool_configuration">7.2.1. Java based data source pool configuration</h4>
|
||
<div class="paragraph">
|
||
<p>It is also possible to override the pool configuration with custom values. Spring Cloud AWS provides a
|
||
<code>org.springframework.cloud.aws.jdbc.config.annotation.RdsInstanceConfigurer</code> that creates a
|
||
<code>org.springframework.cloud.aws.jdbc.datasource.DataSourceFactory</code> which might contain custom pool attributes. The next
|
||
examples shows the implementation of one configurer that overrides the validation query and the initial size.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">@Configuration
|
||
@EnableRdsInstance(dbInstanceIdentifier = "test",password = "secret")
|
||
public class ApplicationConfiguration {
|
||
|
||
@Bean
|
||
public RdsInstanceConfigurer instanceConfigurer() {
|
||
return new RdsInstanceConfigurer() {
|
||
@Override
|
||
public DataSourceFactory getDataSourceFactory() {
|
||
TomcatJdbcDataSourceFactory dataSourceFactory = new TomcatJdbcDataSourceFactory();
|
||
dataSourceFactory.setInitialSize(10);
|
||
dataSourceFactory.setValidationQuery("SELECT 1 FROM DUAL");
|
||
return dataSourceFactory;
|
||
}
|
||
};
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="admonitionblock tip">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-tip" title="Tip"></i>
|
||
</td>
|
||
<td class="content">
|
||
This class returns an anonymous class of type <code>org.springframework.cloud.aws.jdbc.config.annotation.RdsInstanceConfigurer</code>,
|
||
which might also of course be a standalone class.
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_configuring_data_source_in_spring_boot">7.3. Configuring data source in Spring Boot</h3>
|
||
<div class="paragraph">
|
||
<p>The data sources can also be configured using the Spring Boot configuration files. Because of the dynamic number of
|
||
data sources inside one application, the Spring Boot properties must be configured for each data source.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>A data source configuration consists of the general property name <code>cloud.aws.rds.<instanceName></code> for the data source identifier
|
||
following the sub properties for the particular data source where <code>instanceName</code> is the name of the concrete instance. The table below
|
||
outlines all properties for a data source using <code>test</code> as the instance identifier.</p>
|
||
</div>
|
||
<table class="tableblock frame-all grid-all spread">
|
||
<colgroup>
|
||
<col style="width: 33%;">
|
||
<col style="width: 33%;">
|
||
<col style="width: 33%;">
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th class="tableblock halign-left valign-top">property</th>
|
||
<th class="tableblock halign-left valign-top">example</th>
|
||
<th class="tableblock halign-left valign-top">description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">cloud.aws.rds.test</p></td>
|
||
<td class="tableblock halign-left valign-top"></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">The configuration property that configures a data source with the name test</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">cloud.aws.rds.test.password</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">verySecret</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">The password for the db instance test</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">cloud.aws.rds.test.username</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">admin</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">The username for the db instance test (optional)</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">cloud.aws.rds.test.readReplicaSupport</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">If read-replicas should be used for the data source (see below)</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">cloud.aws.rds.test.databaseName</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">fooDb</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Custom database name if the default one from rds should not be used</p></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_read_replica_configuration">7.4. Read-replica configuration</h3>
|
||
<div class="paragraph">
|
||
<p>Amazon RDS allows to use <a href="http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ReadRepl.html">MySQL read-replica</a>
|
||
instances to increase the overall throughput of the database by offloading read data access to one or more read-replica
|
||
slaves while maintaining the data in one master database.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS supports the use of read-replicas in combination with Spring read-only transactions. If the read-replica
|
||
support is enabled, any read-only transaction will be routed to a read-replica instance while using the master database
|
||
for write operations.</p>
|
||
</div>
|
||
<div class="admonitionblock caution">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-caution" title="Caution"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>Using read-replica instances does not guarantee strict <a href="http://en.wikipedia.org/wiki/ACID">ACID</a> semantics for the database
|
||
access and should be used with care. This is due to the fact that the read-replica might be behind and a write might not
|
||
be immediately visible to the read transaction. Therefore it is recommended to use read-replica instances only for transactions that read
|
||
data which is not changed very often and where outdated data can be handled by the application.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The read-replica support can be enabled with the <code>read-replica</code> attribute in the datasource configuration.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans ..>
|
||
<jdbc:data-source db-instance-identifier="RdsSingleMicroInstance"
|
||
password="${rdsPassword}" read-replica-support="true">
|
||
|
||
</jdbc:data-source>
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS will search for any read-replica that is created for the master database and route the read-only transactions
|
||
to one of the read-replicas that are available. A business service that uses read-replicas can be implemented like shown
|
||
in the example.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">@Service
|
||
public class SimpleDatabaseService {
|
||
|
||
private final JdbcTemplate jdbcTemplate;
|
||
|
||
@Autowired
|
||
public SimpleDatabaseService(DataSource dataSource) {
|
||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||
}
|
||
|
||
@Transactional(readOnly = true)
|
||
public Person loadAll() {
|
||
// read data on the read replica
|
||
}
|
||
|
||
@Transactional
|
||
public void updatePerson(Person person) {
|
||
// write data into database
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_failover_support">7.5. Failover support</h3>
|
||
<div class="paragraph">
|
||
<p>Amazon RDS supports a <a href="http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZ.html">Multi-AZ</a> fail-over if
|
||
one availability zone is not available due to an outage or failure of the primary instance. The replication is synchronous
|
||
(compared to the read-replicas) and provides continuous service. Spring Cloud AWS supports a Multi-AZ failover with a retry
|
||
mechanism to recover transactions that fail during a Multi-AZ failover.</p>
|
||
</div>
|
||
<div class="admonitionblock note">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-note" title="Note"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>In most cases it is better to provide direct feedback to a user instead of trying potentially long and frequent retries within
|
||
a user interaction. Therefore the fail-over support is primarily useful for batch application or applications where the
|
||
responsiveness of a service call is not critical.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The Spring Cloud AWS JDBC module provides a retry interceptor that can be used to decorate services with an interceptor.
|
||
The interceptor will retry the database operation again if there is a temporary error due to a Multi-AZ failover. A Multi-AZ
|
||
failover typically lasts only a couple of seconds, therefore a retry of the business transaction will likely succeed.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The interceptor can be configured as a regular bean and then be used by a pointcut expression to decorate the respective
|
||
method calls with the interceptor. The interceptor must have a configured database to retrieve the current status (if it
|
||
is a temporary fail-over or a permanent error) from the Amazon RDS service.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The configuration for the interceptor can be done with a custom element from the Spring Cloud AWS jdbc namespace and
|
||
will be configured like shown:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans ..>
|
||
<jdbc:retry-interceptor id="myInterceptor"
|
||
db-instance-identifier="myRdsDatabase"
|
||
max-number-of-retries="10" />
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The interceptor itself can be used with any Spring advice configuration to wrap the respective service. A pointcut
|
||
for the services shown in the chapter before can be defined as follows:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans ..>
|
||
<aop:config>
|
||
<aop:advisor advice-ref="myInterceptor" pointcut="bean(simpleDatabaseService)" order="1" />
|
||
</aop:config>
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="admonitionblock caution">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-caution" title="Caution"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>It is important that the interceptor is called outside the transaction interceptor to ensure that the whole transaction
|
||
will be re-executed. Configuring the interceptor inside the transaction interceptor will lead to a permanent error because
|
||
the broken connection will never be refreshed.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The configuration above in combination with a transaction configuration will produce the following proxy configuration
|
||
for the service.</p>
|
||
</div>
|
||
<div class="imageblock">
|
||
<div class="content">
|
||
<img src="images/jdbc-retry-interceptor.png" alt="Retry interceptor">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_cloudformation_support">7.6. CloudFormation support</h3>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS supports database instances that are configured with CloudFormation. Spring Cloud AWS can use the logical
|
||
name inside the database configuration and lookup the concrete database with the generated physical resource name. A database
|
||
configuration can be easily configured in CloudFormation with a template definition that might look like the following
|
||
example.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-json" data-lang="json">"myRdsDatabase": {
|
||
"Type": "AWS::RDS::DBInstance",
|
||
"Properties": {
|
||
"AllocatedStorage": "5",
|
||
"DBInstanceClass": "db.t1.micro",
|
||
"DBName": "test",
|
||
"Engine": "mysql",
|
||
"MasterUsername": "admin",
|
||
"MasterUserPassword": {"Ref":"RdsPassword"},
|
||
...
|
||
}
|
||
},
|
||
"readReplicaDatabase": {
|
||
"Type": "AWS::RDS::DBInstance",
|
||
"Properties": {
|
||
"AllocatedStorage" : "5",
|
||
"SourceDBInstanceIdentifier": {
|
||
"Ref": "myRdsDatabase"
|
||
},
|
||
"DBInstanceClass": "db.t1.micro"
|
||
}
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The database can then be configured using the name set in the template. Also, the read-replica can
|
||
be enabled to use the configured read-replica database in the application. A configuration to use the configured
|
||
database is outlined below:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans>
|
||
<aws-context:stack-configuration/>
|
||
|
||
<jdbc:data-source db-instance-identifier="myRdsDatabase" password="${rdsPassword}" read-replica-support="true"/>
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_database_tags">7.7. Database tags</h3>
|
||
<div class="paragraph">
|
||
<p>Amazon RDS instances can also be configured using RDS database specific tags, allowing users to configure database specific
|
||
configuration metadata with the database. Database instance specific tags can be configured using the <code>user-tags-map</code> attribute
|
||
on the <code>data-source</code> element. Configure the tags support like in the example below:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><jdbc:data-source
|
||
db-instance-identifier="myRdsDatabase"
|
||
password="${rdsPassword}" user-tags-map="dbTags" /></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>That allows the developer to access the properties in the code using expressions like shown in the class below:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">public class SampleService {
|
||
|
||
@Value("#{dbTags['aws:cloudformation:aws:cloudformation:stack-name']}")
|
||
private String stackName;
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="admonitionblock note">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-note" title="Note"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>The database tag <code>aws:cloudformation:aws:cloudformation:stack-name</code> is a default tag that is created if the
|
||
database is configured using CloudFormation.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_sending_mails">8. Sending mails</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>Spring has a built-in support to send e-mails based on the <a href="http://www.oracle.com/technetwork/java/javamail/index.html">Java Mail API</a>
|
||
to avoid any static method calls while using the Java Mail API and thus supporting the testability of an application.
|
||
Spring Cloud AWS supports the <a href="http://aws.amazon.com/de/ses/">Amazon SES</a> as an implementation of the Spring Mail abstraction.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>As a result Spring Cloud AWS users can decide to use the Spring Cloud AWS implementation of the Amazon SES service or
|
||
use the standard Java Mail API based implementation that sends e-mails via SMTP to Amazon SES.</p>
|
||
</div>
|
||
<div class="admonitionblock tip">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-tip" title="Tip"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>It is preferred to use the Spring Cloud AWS implementation instead of SMTP mainly for performance reasons.
|
||
Spring Cloud AWS uses one API call to send a mail message, while the SMTP protocol makes multiple requests (EHLO, MAIL FROM, RCPT TO, DATA, QUIT)
|
||
until it sends an e-mail.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_configuring_the_mail_sender">8.1. Configuring the mail sender</h3>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS provides an XML element to configure a Spring <code>org.springframework.mail.MailSender</code> implementation for the
|
||
client to be used. The default mail sender works without a Java Mail dependency and is capable of sending messages without
|
||
attachments as simple mail messages. A configuration with the necessary elements will look like this:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans xmlns:aws-mail="http://www.springframework.org/schema/cloud/aws/mail"
|
||
xsi:schemaLocation="http://www.springframework.org/schema/cloud/aws/mail
|
||
http://www.springframework.org/schema/cloud/aws/mail/spring-cloud-aws-mail-1.0.xsd">
|
||
|
||
<aws-context:context-credentials>
|
||
..
|
||
</aws-context:context-credentials>
|
||
|
||
<aws-context:context-region region="eu-west-1" />
|
||
|
||
<aws-mail:mail-sender id="testSender" />
|
||
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_sending_simple_mails">8.2. Sending simple mails</h3>
|
||
<div class="paragraph">
|
||
<p>Application developers can inject the <code>MailSender</code> into their application code and directly send simple text based e-mail
|
||
messages. The sample below demonstrates the creation of a simple mail message.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">public class MailSendingService {
|
||
|
||
private MailSender mailSender;
|
||
|
||
@Autowired
|
||
public MailSendingService(MailSender mailSender) {
|
||
this.mailSender = mailSender;
|
||
}
|
||
|
||
public void sendMailMessage() {
|
||
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
|
||
simpleMailMessage.setFrom("foo@bar.com");
|
||
simpleMailMessage.setTo("bar@baz.com");
|
||
simpleMailMessage.setSubject("test subject");
|
||
simpleMailMessage.setText("test content");
|
||
this.mailSender.send(simpleMailMessage);
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_sending_attachments">8.3. Sending attachments</h3>
|
||
<div class="paragraph">
|
||
<p>Sending attachments with e-mail requires MIME messages to be created and sent. In order to create MIME messages,
|
||
the Java Mail dependency is required and has to be included in the classpath. Spring Cloud AWS will detect the
|
||
dependency and create a <code>org.springframework.mail.javamail.JavaMailSender</code> implementation that allows to create and
|
||
build MIME messages and send them. A dependency configuration for the Java Mail API is the only change in the configuration
|
||
which is shown below.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><dependency>
|
||
<groupId>javax.mail</groupId>
|
||
<artifactId>mailapi</artifactId>
|
||
<version>1.4.1</version>
|
||
<exclusions>
|
||
<!-- exclusion because we are running on Java 1.7 that includes the activation API by default-->
|
||
<exclusion>
|
||
<artifactId>activation</artifactId>
|
||
<groupId>javax.activation</groupId>
|
||
</exclusion>
|
||
</exclusions>
|
||
</dependency></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="admonitionblock note">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-note" title="Note"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>Even though there is a dependency to the Java Mail API there is still the Amazon SES API used underneath to send mail
|
||
messages. There is no <a href="http://docs.aws.amazon.com/ses/latest/DeveloperGuide/send-email-smtp.html">SMTP setup</a> required
|
||
on the Amazon AWS side.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Sending the mail requires the application developer to use the <code>JavaMailSender</code> to send an e-mail as shown in the example
|
||
below.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">public class MailSendingService {
|
||
|
||
private JavaMailSender mailSender;
|
||
|
||
@Autowired
|
||
public MailSendingService(JavaMailSender mailSender) {
|
||
this.mailSender = mailSender;
|
||
}
|
||
|
||
public void sendMailMessage() {
|
||
this.mailSender.send(new MimeMessagePreparator() {
|
||
|
||
@Override
|
||
public void prepare(MimeMessage mimeMessage) throws Exception {
|
||
MimeMessageHelper helper =
|
||
new MimeMessageHelper(mimeMessage, true, "UTF-8");
|
||
helper.addTo("foo@bar.com");
|
||
helper.setFrom("bar@baz.com");
|
||
helper.addAttachment("test.txt", ...);
|
||
helper.setSubject("test subject with attachment");
|
||
helper.setText("mime body", false);
|
||
}
|
||
});
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_configuring_regions">8.4. Configuring regions</h3>
|
||
<div class="paragraph">
|
||
<p>Amazon SES is not available in all <a href="http://docs.aws.amazon.com/ses/latest/DeveloperGuide/regions.html">regions</a> of the
|
||
Amazon Web Services cloud. Therefore an application hosted and operated in a region that does not support the mail
|
||
service will produce an error while using the mail service. Therefore the region must be overridden for the mail
|
||
sender configuration. The example below shows a typical combination of a region (EU-CENTRAL-1) that does not provide
|
||
an SES service where the client is overridden to use a valid region (EU-WEST-1).</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans ...>
|
||
|
||
<aws-context:context-region region="eu-central-1" />
|
||
<aws-mail:mail-sender id="testSender" region="eu-west-1"/>
|
||
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_authenticating_e_mails">8.5. Authenticating e-mails</h3>
|
||
<div class="paragraph">
|
||
<p>To avoid any spam attacks on the Amazon SES mail service, applications without production access must
|
||
<a href="http://docs.aws.amazon.com/ses/latest/DeveloperGuide/verify-email-addresses.html">verify</a> each
|
||
e-mail receiver otherwise the mail sender will throw a <code>com.amazonaws.services.simpleemail.model.MessageRejectedException</code>.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p><a href="http://docs.aws.amazon.com/ses/latest/DeveloperGuide/request-production-access.html">Production access</a> can be requested
|
||
and will disable the need for mail address verification.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_resource_handling">9. Resource handling</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>The Spring Framework provides a <code>org.springframework.core.io.ResourceLoader</code> abstraction to load files from the filesystem,
|
||
servlet context and the classpath. Spring Cloud AWS adds support for the <a href="http://aws.amazon.com/s3/">Amazon S3</a> service
|
||
to load and write resources with the resource loader and the <code>s3</code> protocol.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The resource loader is part of the context module, therefore no additional dependencies are necessary to use the resource
|
||
handling support.</p>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_configuring_the_resource_loader">9.1. Configuring the resource loader</h3>
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS does not modify the default resource loader unless it encounters an explicit configuration with an XML namespace element.
|
||
The configuration consists of one element for the whole application context that is shown below:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
xmlns:aws-context="http://www.springframework.org/schema/cloud/aws/context"
|
||
xsi:schemaLocation="http://www.springframework.org/schema/cloud/aws/context
|
||
http://www.springframework.org/schema/cloud/aws/context/spring-cloud-aws-context-1.0.xsd">
|
||
|
||
<aws-context:context-credentials>
|
||
...
|
||
</aws-context:context-credentials>
|
||
|
||
<aws-context:context-resource-loader/>
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_downloading_files">9.2. Downloading files</h3>
|
||
<div class="paragraph">
|
||
<p>Downloading files can be done by using the <code>s3</code> protocol to reference Amazon S3 buckets and objects inside their bucket. The
|
||
typical pattern is <code>s3://<bucket>/<object></code> where bucket is the global and unique bucket name and object is a valid object
|
||
name inside the bucket. The object name can be a file in the <em>root</em> folder of a bucket or a nested file within a directory
|
||
inside a bucket.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The next example demonstrates the use of the resource loader to load different resources.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">public class SimpleResourceLoadingBean {
|
||
|
||
@Autowired
|
||
private ResourceLoader resourceLoader;
|
||
|
||
public void resourceLoadingMethod() throws IOException {
|
||
Resource resource = this.resourceLoader.getResource("s3://myBucket/rootFile.log");
|
||
Resource secondResource = this.resourceLoader.getResource("s3://myBucket/rootFolder/subFile");
|
||
|
||
InputStream inputStream = resource.getInputStream();
|
||
//read file
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_uploading_files">9.3. Uploading files</h3>
|
||
<div class="paragraph">
|
||
<p>Since Spring Framework 3.1 the resource loader can also be used to upload files with the <code>org.springframework.core.io.WritableResource</code>
|
||
interface which is a specialization of the <code>org.springframework.core.io.ResourceLoader</code> interface. Clients can upload files
|
||
using the <code>WritableResource</code> interface. The next example demonstrates an upload of a resource using the resource loader.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">public class SimpleResourceLoadingBean {
|
||
|
||
@Autowired
|
||
private ResourceLoader resourceLoader;
|
||
|
||
public void writeResource() throws IOException {
|
||
Resource resource = this.resourceLoader.getResource("s3://myBucket/rootFile.log");
|
||
WritableResource writableResource = (WritableResource) resource;
|
||
try (OutputStream outputStream = writableResource.getOutputStream()) {
|
||
outputStream.write("test".getBytes());
|
||
}
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_uploading_multi_part_files">9.3.1. Uploading multi-part files</h4>
|
||
<div class="paragraph">
|
||
<p>Amazon S3 supports <a href="http://docs.aws.amazon.com/AmazonS3/latest/dev/uploadobjusingmpu.html">multi-part uploads</a> to
|
||
increase the general throughput while uploading. Spring Cloud AWS by default only uses one thread to upload the files and
|
||
therefore does not provide parallel upload support. Users can configure a custom <code>org.springframework.core.task.TaskExecutor</code>
|
||
for the resource loader. The resource loader will queue multiple threads at the same time to use parallel multi-part uploads.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The configuration for a resource loader that uploads with 10 Threads looks like the following</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-xml" data-lang="xml"><beans ...>
|
||
<aws-context:context-resource-loader task-executor="executor" />
|
||
<task:executor id="executor" pool-size="10" queue-capacity="0" rejection-policy="CALLER_RUNS" />
|
||
</beans></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="admonitionblock warning">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-warning" title="Warning"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>Spring Cloud AWS consumes up to 5 MB (at a minimum) of memory per thread. Therefore each parallel thread will incur
|
||
a memory footprint of 5 MB in the heap, and a thread size of 10 will consume therefore up to 50 mb of heap space. Spring Cloud
|
||
AWS releases the memory as soon as possible. Also, the example above shows that there is no <code>queue-capacity</code> configured,
|
||
because queued requests would also consume memory.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_uploading_with_the_transfermanager">9.3.2. Uploading with the TransferManager</h4>
|
||
<div class="paragraph">
|
||
<p>The Amazon SDK also provides a high-level abstraction that is useful to upload files, also with multiple threads using
|
||
the multi-part functionality. A <code>com.amazonaws.services.s3.transfer.TransferManager</code> can be easily created in the application
|
||
code and injected with the pre-configured <code>com.amazonaws.services.s3.AmazonS3</code> client that is already created
|
||
with the Spring Cloud AWS resource loader configuration.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>This example shows the use of the <code>transferManager</code> within an application to upload files from the hard-drive.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">public class SimpleResourceLoadingBean {
|
||
|
||
@Autowired
|
||
private AmazonS3 amazonS3;
|
||
|
||
public void withTransferManager() {
|
||
TransferManager transferManager = new TransferManager(this.amazonS3);
|
||
transferManager.upload("myBucket","filename",new File("someFile"));
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_searching_resources">9.4. Searching resources</h3>
|
||
<div class="paragraph">
|
||
<p>The Spring resource loader also supports collecting resources based on an Ant-style path specification. Spring Cloud AWS
|
||
offers the same support to resolve resources within a bucket and even throughout buckets. The next example shows the resource
|
||
resolution by using different patterns.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">public class SimpleResourceLoadingBean {
|
||
|
||
@Autowired
|
||
private ResourcePatternResolver resourcePatternResolver;
|
||
|
||
public void resolveAndLoad() throws IOException {
|
||
Resource[] allTxtFilesInFolder = this.resourcePatternResolver.getResources("s3://bucket/name/*.txt");
|
||
Resource[] allTxtFilesInBucket = this.resourcePatternResolver.getResources("s3://bucket/**/*.txt");
|
||
Resource[] allTxtFilesGlobally = this.resourcePatternResolver.getResources("s3://**/*.txt");
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="admonitionblock warning">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-warning" title="Warning"></i>
|
||
</td>
|
||
<td class="content">
|
||
<div class="paragraph">
|
||
<p>Resolving resources throughout all buckets can be very time consuming depending on the number of buckets a user owns.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_using_cloudformation_3">9.5. Using CloudFormation</h3>
|
||
<div class="paragraph">
|
||
<p>CloudFormation also allows to create buckets during stack creation. These buckets will typically have a generated name
|
||
that must be used as the bucket name. In order to allow application developers to define <em>static</em> names inside their
|
||
configuration, Spring Cloud AWS provides support to resolve the generated bucket names.
|
||
Application developers can use the <code>org.springframework.cloud.aws.core.env.ResourceIdResolver</code> interface to resolve the
|
||
physical names that are generated based on the logical names.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The next example shows a bucket definition inside a CloudFormation stack template. The bucket will be created with a name
|
||
like <em>integrationteststack-sampleBucket-23qysofs62tc2</em></p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-json" data-lang="json">{
|
||
"Resources": {
|
||
"sampleBucket": {
|
||
"Type": "AWS::S3::Bucket"
|
||
}
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Application developers can resolve that name and use it to load resources as shown in the next example below.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="highlight"><code class="language-java" data-lang="java">public class SimpleResourceLoadingBean {
|
||
|
||
private final ResourceLoader loader;
|
||
private final ResourceIdResolver idResolver;
|
||
|
||
@Autowired
|
||
public SimpleResourceLoadingBean(ResourceLoader loader, ResourceIdResolver idResolver) {
|
||
this.loader = loader;
|
||
this.idResolver = idResolver;
|
||
}
|
||
|
||
public void resolveAndLoad() {
|
||
String sampleBucketName = this.idResolver.
|
||
resolveToPhysicalResourceId("sampleBucket");
|
||
Resource resource = this.loader.
|
||
getResource("s3://" + sampleBucketName + "/test");
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="footer">
|
||
<div id="footer-text">
|
||
Last updated 2016-08-18 14:03:30 +02:00
|
||
</div>
|
||
</div>
|
||
</body>
|
||
</html> |