blob: c675f0f9399c6a39c5cce07aad342412789e8fb3 [file] [log] [blame]
<!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.3">
<meta name="author" content="Daniel Marthaler">
<title>Virgo Snaps User Guide</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,700">
<style>
/* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */
/* Remove comment around @import statement below when using 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,700";*/
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}
.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}
body{-webkit-font-smoothing:antialiased}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.center{margin-left:auto;margin-right:auto}
.spread{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}
body{tab-size:4}
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{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;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:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;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 li{line-height:1.3334;margin-top:.3334em}
#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;margin-bottom:.8rem;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;line-height:1.45}
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:-.025em;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.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,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
.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;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.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 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;text-indent:-1.05em;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}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,span.alt{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,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
svg{max-width:100%}
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>
</head>
<body class="article">
<div id="header">
<h1>Virgo Snaps User Guide</h1>
<div class="details">
<span id="author" class="author">Daniel Marthaler</span><br>
<span id="revdate">2017-02-04</span>
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Christopher Frost<br>
Daniel Marthaler</p>
</div>
<div class="paragraph">
<p><strong>Virgo Snaps 3.7.1.SNAPSHOT</strong> <span class="image" style="float: right"><img src="images/virgo-logo-large.png" alt="Eclipse Virgo" width="300"></span></p>
</div>
<div class="paragraph">
<p>VMware Inc. - initial contribution<br>
Copyright &#169; 2009, 2011 VMware Inc. and others</p>
</div>
<hr>
</div>
</div>
<div class="sect1">
<h2 id="introduction">1. Introduction</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This Guide covers Virgo Snaps and assumes you are already
familiar with OSGi and Eclipse Virgo. See <a href="#further-reading">here</a>.</p>
</div>
<div class="sect2">
<h3 id="introduction-concepts">1.1. Concepts</h3>
<div class="paragraph">
<p>Using Virgo Snaps, or VS for short, you
can construct a web application from multiple OSGi bundles, each of
which serves up content for a distinct sub-portion of your application&#8217;s
URL space. VS applications are arranged in a
parent/child structure, with each application having at most one
top-level parent, and zero or more children/grand children and so on.
Parents are referred to as the hosts, children as snaps.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/snapsAndHosts.png" alt="snapsAndHosts">
</div>
</div>
<div class="paragraph">
<p>Here you see that any <code>HTTP</code> requests for '/app' will be dealt with by
the host bundle while '/app/orders' and '/app/billing' will be dealt
with by their respective snaps bundles. Each host/snap can use
completely different web technologies to deal with the requests,
Virgo Snaps places no restrictions on your choices. The
developer decides how to split the application up and how closely to
couple the host and various snaps. VS has been
designed to be as un-invasive as possible while still making it easy to
share information between the host and its snaps.</p>
</div>
</div>
<div class="sect2">
<h3 id="introduction-prereqs">1.2. Prerequisites</h3>
<div class="paragraph">
<p>VS requires Virgo Server for Apache Tomcat and Java SE 6
or later to be installed. Java is available from <a href="http://www.java.com/" class="bare">http://www.java.com/</a>
and elsewhere. Virgo Server for Apache Tomcat is available from
<a href="http://www.eclipse.org/virgo/download/">here</a>.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="installation">2. Installing Virgo Snaps</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="installation-zip">2.1. Installing from the Zip Download</h3>
<div class="paragraph">
<p>VS is distributed as a Zip file. This can be
downloaded from <a href="http://www.eclipse.org/virgo/download/">here</a>.</p>
</div>
<div class="paragraph">
<p>Once the Zip file is unpacked you will find several things inside,
including a copy of this documentation in the <code>docs</code> folder. The layout
is shown below.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/snapsZipFile.png" alt="snapsZipFile">
</div>
</div>
<div class="paragraph">
<p>Take the bundles and plan in the <code>dist</code> folder and place them in either
the <code>/repository/usr</code> or <code>/repository/ext</code> folder of your Virgo install.
Later you need the bundles in the <code>sample</code> folder. If you wish to view
the source for VS or its sample, then these are
in the <code>src</code> folder.</p>
</div>
<div class="paragraph">
<p>In order to start the Snaps system as a part of your
Virgo Server for Apache Tomcat, the Snaps plan needs to be referenced from the
<code>initialArtifacts</code> property. This is located in the config file at
<code>${SERVER_HOME}/config/org.eclipse.virgo.kernel.userregion.properties</code>.
The property should be extended like this.</p>
</div>
<div class="literalblock">
<div class="content">
<pre>initialArtifacts=repository:plan/org.eclipse.virgo.kernel.userregion.springdm,
repository:plan/org.eclipse.virgo.web.tomcat,
repository:plan/org.eclipse.virgo.snaps</pre>
</div>
</div>
<div class="paragraph">
<p>Now you are ready to check that Snaps is working.</p>
</div>
</div>
<div class="sect2">
<h3 id="installation-post">2.2. Verifying it&#8217;s working</h3>
<div class="paragraph">
<p>The sample application in the Zip file demonstrates all of the features
of Snaps. You will cover this application in detail later, but for now
install it to verify that you have a working Snaps install. Start the
server up and check the output it produces contains the following:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>&lt;DE0004I&gt; Starting plan 'org.eclipse.virgo.snaps' version '3.0.0'.
&lt;DE0004I&gt; Starting bundle 'org.eclipse.virgo.snaps.api' version '[version]'.
&lt;DE0005I&gt; Started bundle 'org.eclipse.virgo.snaps.api' version '[version]'.
&lt;DE0004I&gt; Starting bundle 'org.eclipse.virgo.snaps.core' version '[version]'.
&lt;DE0005I&gt; Started bundle 'org.eclipse.virgo.snaps.core' version '[version]'.
&lt;DE0005I&gt; Started plan 'org.eclipse.virgo.snaps' version '3.0.0'.</pre>
</div>
</div>
<div class="paragraph">
<p>Open up the Virgo Admin Console by navigating to
<code><a href="http://localhost:8080/admin" class="bare">http://localhost:8080/admin</a></code> and then go to the artifacts tab to deploy
some new bundles. The first bundle to deploy is located at
<code>/sample/org.eclipse.virgo.snaps.sample.animal-[version].jar</code>,
select it in the browser and upload. In the terminal window you started
the Virgo Server for Apache Tomcat in you should see the usual messages about the
bundle starting and this message:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>&lt;SN0000I&gt; Host created for path '/animal-menu-bar'</pre>
</div>
</div>
<div class="paragraph">
<p>This shows that the host has been registered and is ready for Snaps to
attach to it. To view the application take your browser to
<code><a href="http://localhost:8080/animal-menu-bar/" class="bare">http://localhost:8080/animal-menu-bar/</a></code>. You should be presented with a
green looking page that has a menu bar with two links on it. One will
take you to the page you are currently on while the other goes back to
the admin page.</p>
</div>
<div class="paragraph">
<p>This is all very nice, but it&#8217;s time to deploy another snap that will
dynamicaly add some extra content to the app. The next bundle to deploy
is located at
<code>/sample/org.eclipse.virgo.snaps.sample.dog-[version].jar</code>.
Deploy it the same way as the last one using the admin console and check
the messages to verify it has been bound to the host:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>&lt;SN0010I&gt; Snap bound to path '/animal-menu-bar/dog'</pre>
</div>
</div>
<div class="paragraph">
<p>In your browser, go back to the application and refresh the page and a
new menu item should now be present. If you click it a new page will
load with content from the newly installed snap. In this case a picture
of a dog.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/sampleDog.png" alt="sampleDog">
</div>
</div>
<div class="paragraph">
<p>You have now verified that your install is working and can continue on
to learn how Snaps works and how you can use it to break up your web
applications.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="using-snaps">3. Using Virgo Snaps in your application</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Every snap or host should also be a regular WAB (Web Application
Bundle), all you have to do is add some additional configuration to
benefit from VS.</p>
</div>
<div class="sect2">
<h3 id="using-snaps-configuring-the-host">3.1. Configuring the Host</h3>
<div class="paragraph">
<p>In the WAB that is to act as a host to Snaps bundles, all that needs to
be added is a filter in the host&#8217;s <code>web.xml</code>. This filter can be
mapped to any sub path that you want forwarded to registered snaps. It
is important to note the extra <code>dispatcher</code> declarations in the
<code>filter-mapping</code>. Without these internal lookups, resources like JSPs
won&#8217;t get passed to the appropriate snap when needed.</p>
</div>
<div class="literalblock">
<div class="content">
<pre>&lt;filter&gt;
&lt;filter-name&gt;host-filter-name&lt;/filter-name&gt;
&lt;filter-class&gt;org.eclipse.virgo.snaps.core.SnapHostFilter&lt;/filter-class&gt;
&lt;/filter&gt;
&lt;filter-mapping&gt;
&lt;filter-name&gt;host-filter-name&lt;/filter-name&gt;
&lt;url-pattern&gt;/*&lt;/url-pattern&gt;
&lt;dispatcher&gt;INCLUDE&lt;/dispatcher&gt;
&lt;dispatcher&gt;FORWARD&lt;/dispatcher&gt;
&lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;
&lt;/filter-mapping&gt;</pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="using-snaps-configuring-a-snap">3.2. Configuring a Snap</h3>
<div class="paragraph">
<p>A snap is a normal WAB with the addition of two extra manifest headers.
The <code>Snap-Host</code> is used to resolve the WAB you want to act as a
host for this snap. The <code>Snap-ContextPath</code> gives the path the
snap will answer to. In order to reach the snap a request must be made
for the host&#8217;s context path, plus any path in the host&#8217;s Snaps filter
followed by the path given with the <code>Snap-ContextPath</code> header.</p>
</div>
<div class="literalblock">
<div class="content">
<pre>Snap-Host: org.eclipse.virgo.snaps.sample.animal;version="${version:[=.=.=, =.+1)}"
Snap-ContextPath: /dog</pre>
</div>
</div>
<div class="paragraph">
<p>For attaching a snap to multiple hosts, the <code>Snap-Host</code> allows the specification of
multiple host bundles. Given the example above, the Dog snap could be additionally
attached to the <code>Snap-Host</code> <code>org.eclipse.virgo.snaps.sample.zoo</code> as follows:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>Snap-Host: org.eclipse.virgo.snaps.sample.animal,org.eclipse.virgo.snaps.sample.zoo
Snap-ContextPath: /dog</pre>
</div>
</div>
<div class="paragraph">
<p>Even though a snap can be attached to multiple hosts, the <code>Snap-ContextPath</code> will
remain the same, which means that regardless of the <code>Snap-Host</code>, the snap
will be located under the same path. Multiple <code>Snap-Host</code> definitions might
be useful for snaps that hold resources required in multiple applications (for instance
third party libraries or functionality that can be reused across applications).</p>
</div>
</div>
<div class="sect2">
<h3 id="using-snaps-taglibs">3.3. Using the Snaps taglibs</h3>
<div class="paragraph">
<p>There is a tag library available that makes information about the Snaps
environment available to the host from within a JSP page. The prefix and
URI to declare are
<code>&lt;%@ taglib prefix="snaps" uri="http://www.eclipse.org/virgo/snaps"
%&gt;</code>. You can now access an array of all the Snaps currently
registered with the host. Each snap in the array has two values, the
first is the context path the Snap is registered with, as given in the
snaps manifest header. The second is an array of properties, if any,
given in a property file in the snap. The file must be in the <code>META-INF</code>
directory and have a name of <code>snap.properties</code>. The following code snippet
shows all of these in use to produce links to each of the installed snaps.</p>
</div>
<div class="literalblock">
<div class="content">
<pre>&lt;snaps:snaps var="snaps"&gt;
&lt;c:forEach var="snap" items="${snaps}"&gt;
&lt;a href="&lt;c:url value="${snap.contextPath}${snap.properties['link.path']}"/&gt;"&gt;
${snap.properties['link.text']}&lt;/a&gt;
&lt;/c:forEach&gt;
&lt;/snaps:snaps&gt;</pre>
</div>
</div>
<div class="paragraph">
<p>The first line uses the Snaps tag library to place the array of snaps
into the <code>snaps</code> variable. Then a <code>forEach</code> loop goes through each snap.
The content of the <code>forEach</code> loop is the really interesting bit. The
first variable <code>${snap.contextPath}</code> returns the context path of the
snap. The two lookups are for properties
<code>${snap.properties['something']}</code>. They rely on the snap having the
properties file in place with <code>link.path</code> and <code>link.text</code> defined in it.
This shows the flexibility you have for defining your own contract
between the host and its snaps. In this case each snap can contribute
extra information about how links in a menu should be constructed.</p>
</div>
</div>
<div class="sect2">
<h3 id="using-snaps-host-prefix">3.4. Referencing Resources</h3>
<div class="paragraph">
<p>If the snap needs to lookup any resources, this can be done in the
normal way and if the resource cannot be found in the snap, then the
host will be checked. Remember that a host and all its snaps use a
single <code>ServletContext</code>. As the snap is always checked first it can hide
resources at the same path in its host. So if you want to look up a
resource in the snaps host that exists in the snap simply prefix the
path with <code>host:</code>. This will then cause the Snaps system to bypass the
snap and look only in its host for the requested resource. If it is not
found in its host the snap will NOT be checked, the lookup will return
with nothing.</p>
</div>
<div class="literalblock">
<div class="content">
<pre>request.getServletContext().getResource("host:/WEB-INF/sample.properties");</pre>
</div>
</div>
<div class="paragraph">
<p>This line of Java code gets the ServletContext from the HttpRequest
object and then tries to get a resource from the host, bypassing the
snap.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="sample-application">4. A Sample Application</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The Virgo Snaps Zip file comes with a sample application that
you may have already deployed to verify your VS
install. In this chapter you will look at it in more detail to see how
an application can be composed and see all the Snaps features in action.</p>
</div>
<div class="sect2">
<h3 id="sample-application-structure">4.1. Structure of the sample application</h3>
<div class="paragraph">
<p>The application is very simple, it consists of just a front end,
composed of four bundles, a host with three Snaps.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/sampleApp.png" alt="sampleApp">
</div>
</div>
<div class="paragraph">
<p>The host and three snaps show their <code>Snap-ContextPaths</code>. The Cat and Dog
snaps both contribute content while the final snap just contributes
styling information in the form of <code>css</code> files. The following sections
look at each of these in detail.</p>
</div>
</div>
<div class="sect2">
<h3 id="sample-application-host">4.2. The Host bundle</h3>
<div class="paragraph">
<p>This bundle is very simple with no Java code. It defines the Snaps
filter in its <code>web.xml</code> just as you have seen in the
<a href="#using-snaps-configuring-the-host">Using Snaps</a> section. The menu
bar uses the taglibs support to render links for each registered Snap.</p>
</div>
<div class="literalblock">
<div class="content">
<pre>&lt;ul&gt;
&lt;li&gt;&lt;a href="&lt;c:url value="/"/&gt;"&gt;Home&lt;/a&gt;&lt;/li&gt;
&lt;snaps:snaps var="snaps"&gt;
&lt;c:forEach var="snap" items="${snaps}"&gt;
&lt;c:if test="${snap.contextPath ne '/styles'}"&gt;
&lt;li&gt;&lt;a href="&lt;c:url value="${snap.contextPath}${snap.properties['link.path']}"/&gt;"&gt;
${snap.properties['link.text']}&lt;/a&gt;
&lt;/li&gt;
&lt;/c:if&gt;
&lt;/c:forEach&gt;
&lt;/snaps:snaps&gt;
&lt;/ul&gt;</pre>
</div>
</div>
<div class="paragraph">
<p>This is very similar to an example you have already seen. It puts the
links in a list and adds a link at the beginning to return to the host.
The properties it is referencing are located in the snaps themselves at
<code>/META-INF/snap.properties</code>.</p>
</div>
</div>
<div class="sect2">
<h3 id="sample-application-new-content">4.3. Adding New Content</h3>
<div class="paragraph">
<p>If you now deploy both the Dog and Cat snaps, two new items will appear
on the menu bar, each providing content from their respective snaps.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/sampleCat.png" alt="sampleCat">
</div>
</div>
<div class="paragraph">
<p>If you look at the Cat page you will see it is also displaying some
properties. The Cat Snap is a little more complex, it has it&#8217;s own
<code>DispatcherServlet</code> defined in its <code>web.xml</code> and a controller defined
using annotations. If you look at the controller code you will see where
these two properties come from.</p>
</div>
<div class="literalblock">
<div class="content">
<pre>@Controller
public class CatController {
@RequestMapping("/meow")
public ModelAndView cat(HttpServletRequest request) throws IOException {
URL host = request.getServletContext().getResource("host:/WEB-INF/sample.properties");
Properties host_props = new Properties();
if(host != null){
host_props.load(host.openStream());
}
URL snap = request.getServletContext().getResource("/WEB-INF/sample.properties");
Properties snap_props = new Properties();
if(snap != null){
snap_props.load(snap.openStream());
}
return new ModelAndView("index").addObject("host", host_props.getProperty("some.property"))
.addObject("snap", snap_props.getProperty("some.property"));
}
}</pre>
</div>
</div>
<div class="paragraph">
<p>There are two properties files called <code>sample.properties</code> located at the
same path within both the Cat snap and the host. Each contains a
property called <code>some.property</code> but with distinct value. Looking at the
code above you can see the <code>host:</code> prefix being used to ensure the first
lookup only looks in the host and skips the snap. These values are then
placed in the Model for the JSP page to render.</p>
</div>
</div>
<div class="sect2">
<h3 id="sample-application-dynamic-styling">4.4. Re-styling</h3>
<div class="paragraph">
<p>Finally the Blue styling snap shows how Snaps can be used in other ways.
Deploying this will cause all requests to <code>/styles</code> to be redirected and
the application will change in appearance but not content. All this
happens without re-deploying the host bundle, although you may need to
do a full refresh to clear your browser&#8217;s cache.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/sampleBlue.png" alt="sampleBlue">
</div>
</div>
<div class="paragraph">
<p>This is the same page as before, being rendered by the host and the Cat
snap, but with the style information now coming from the Blue snap.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="further-reading">5. Further Reading</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The User and Programmer Guides provide a lot of information about using
and developing applications for the Virgo Server.</p>
</div>
<div class="paragraph">
<p><a href="http://www.eclipse.org/virgo/documentation/">Virgo Documentation
page</a></p>
</div>
<div class="paragraph">
<p><a href="../../virgo-programmer-guide/html/index.html">Virgo Server for Apache Tomcat
Programmer Guide</a></p>
</div>
<div class="paragraph">
<p><a href="../../virgo-user-guide/html/index.html">Virgo Server for Apache Tomcat User
Guide</a></p>
</div>
<div class="paragraph">
<p><a href="http://www.slideshare.net/cgfrost/eclipsecon2011-virgo-snaps">Slides from
a presentation at EclipseCon 2011 about Virgo Snaps.</a></p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="known-issues">6. Known Issues</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This section describes known issues that you might run into, along with
corresponding workarounds.</p>
</div>
<div class="sect2">
<h3 id="known-issues-jetty-restriction">6.1. Virgo Jetty Server Restriction</h3>
<div class="paragraph">
<p>Virgo Snaps is not supported on the Virgo Jetty Server,
please use with the Virgo Server for Apache Tomcat.</p>
</div>
</div>
</div>
</div>
</div>
</body>
</html>