ode-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] Apache Ode: Jacob (page edited)
Date Fri, 05 Oct 2007 17:00:00 GMT
<html>
<head>
    <base href="http://cwiki.apache.org/confluence" />
    <style type="text/css">
    <!--
    body, p, td, table, tr, .bodytext, .stepfield {
	font-family: Verdana, arial, sans-serif;
	font-size: 11px;
	line-height: 16px;
	color: #000000;
	font-weight: normal;
}
#PageContent {
	text-align: left;
	background-color: #fff;
	padding: 0px;
	margin: 0px;
    padding-bottom:20px;
}
/*
** when this stylesheet is used for the Tiny MCE Wysiwyg editor's edit area, we can't
** use an id=PageContent or class=wiki-content, so we must
** set the body style to that used for PageContent, and p to that used for wiki-content.
*/

body {
	margin: 0px;
	padding: 0px;
	text-align: center;
    background-color: #f0f0f0;
}

@media print {

body {
    background-color: #fff;
}

}

.monospaceInput {
    font:12px monospace
}

.wiki-content p, .commentblock p {
    margin: 16px 0px 16px 0px;
    padding: 0px;
}

.wiki-content-preview {
    padding: 5px;
    border-left: 1px solid #3c78b5;
    border-right: 1px solid #3c78b5;
}

ul, ol {
    margin-top: 2px;
    margin-bottom: 2px;
    padding-top: 0px;
    padding-bottom: 0px;
}

pre {
    padding: 0px;
    margin-top: 5px;
    margin-left: 15px;
    margin-bottom: 5px;
    margin-right: 5px;
    text-align: left;
}

.helpheading {
    font-weight: bold;
    background-color: #D0D9BD;
        border-bottom: 1px solid #3c78b5;
        padding: 4px 4px 4px 4px;
        margin: 0px;
        margin-top: 10px;
}
.helpcontent {
        padding: 4px 4px 20px 4px;
    background-color: #f5f7f1;
}

.code {
 	border: 1px dashed #3c78b5;
    font-size: 11px;
	font-family: Courier;
    margin: 10px;
	line-height: 13px;
}

.focusedComment {
    background: #ffffce;
}

.commentBox, .focusedComment {
    padding: 10px;
    margin: 5px 0 5px 0;
    border: 1px #bbb solid;
}

.codeHeader {
    background-color: #f0f0f0;
 	border-bottom: 1px dashed #3c78b5;
    padding: 3px;
	text-align: center;
}

.codeContent {
    text-align: left;
    background-color: #f0f0f0;
    padding: 3px;
}

.preformatted {
 	border: 1px dashed #3c78b5;
    font-size: 11px;
	font-family: Courier;
    margin: 10px;
	line-height: 13px;
}

.preformattedHeader {
    background-color: #f0f0f0;
 	border-bottom: 1px dashed #3c78b5;
    padding: 3px;
	text-align: center;
}

.preformattedContent {
    background-color: #f0f0f0;
    padding: 3px;
}

.panel {
 	border: 1px dashed #3c78b5;
    margin: 10px;
    margin-top: 0px;
}

.panelHeader {
    background-color: #f0f0f0;
 	border-bottom: 1px dashed #3c78b5;
    padding: 3px;
	text-align: center;
}

.panelContent {
    background-color: #f0f0f0;
    padding: 5px;
}

.anonymousAlert {
    background-color: #f0f0f0;
 	border: 1px dashed red;
    font-size: 11px;
    padding: 10px 5px 10px 5px;
    margin: 4px;
	line-height: 13px;
}

.lockAlert {
    background-color: #f0f0f0;
    width: 50%;
 	border: 1px dashed red;
    font-size: 11px;
    padding: 10px 5px 10px 5px;
    margin: 4px;
	line-height: 13px;
}


.code-keyword {
  color: #000091;
  background-color: inherit;
}

.code-object {
  color: #910091;
  background-color: inherit;
}

.code-quote {
  color: #009100;
  background-color: inherit;
}

.code-comment {
  color: #808080;
  background-color: inherit;
}


.code-xml .code-keyword {
  color: inherit;
  font-weight: bold;
}

.code-tag {
  color: #000091;
  background-color: inherit;
}

.breadcrumbs {
    background-color: #f0f0f0;
 	border-color: #3c78b5;
	border-width: 1px 0px 1px 0px;
	border-style: solid;
    font-size: 11px;
    padding: 3px 0px 3px 0px;
}

.navmenu {
    border: 1px solid #ccc;
}

.menuheading {
    font-weight: bold;
    background-color: #f0f0f0;
 	border-bottom: 1px solid #3c78b5;
	padding: 4px 4px 2px 4px;
}

.menuitems {
	padding: 4px 4px 20px 4px;
}

.rightpanel {
    border-left: 1px solid #ccc;
    border-bottom: 1px solid #ccc;
}

#helpheading {
    text-align: left;
    font-weight: bold;
    background-color: #D0D9BD;
 	border-bottom: 1px solid #3c78b5;
	padding: 4px 4px 4px 4px;
	margin: 0px;
}
#helpcontent {
	padding: 4px 4px 4px 4px;
    background-color: #f5f7f1;
}
.helptab-unselected {
    font-weight: bold;
	padding: 5px;
    background-color: #f5f7f1;
}
.helptab-selected {
    font-weight: bold;
    background-color: #D0D9BD;
	padding: 5px;
}
.helptabs {
    margin: 0px;
    background-color: #f5f7f1;
	padding: 5px;
}
.infopanel-heading {
    font-weight: bold;
	padding: 4px 0px 2px 0px;
}

.pagebody {
}

.pageheader {
	padding: 5px 5px 5px 0px;
 	border-bottom: 1px solid #3c78b5;
}

.pagetitle {
	font-size: 22px;
	font-weight: bold;
	font-family: Arial, sans-serif;
	color: #003366;
}

.newpagetitle {
    color: #ccc !important;
}

.steptitle {
	font-size: 18px;
	font-weight: bold;
	font-family: Arial, sans-serif;
	color: #003366;
	margin-bottom: 7px;
}

.substeptitle {
    font-size: 12px;
    font-weight: bold;
    font-family: Arial, sans-serif;
    color: #003366;
    margin: 2px 4px 4px 4px;
    padding: 2px 4px 1px 4px;
}

.stepdesc {
    font-family: Verdana, arial, sans-serif;
	font-size: 11px;
	line-height: 16px;
	font-weight: normal;
    color: #666666;
    margin-top: 7px;
    margin-bottom: 7px;
}

.steplabel {
    font-weight: bold;
    margin-right: 4px;
    color: black;
    float: left;
    width: 15%;
    text-align: right;
}

.stepfield {
    background: #f0f0f0;
    padding: 5px;
}

.submitButtons{
    margin-top:5px;
    text-align:right;
}

.formtitle {
	font-size: 12px;
	font-weight: bold;
	font-family: Arial, sans-serif;
	color: #003366;
}

.sectionbottom {
    border-bottom: 1px solid #3c78b5;
}

.topRow {
    border-top: 2px solid #3c78b5;
}

.tabletitle {
	font-size: 14px;
	font-weight: bold;
	font-family: Arial, sans-serif;
    padding: 3px 0px 2px 0px;
    margin: 8px 4px 2px 0px;
	color: #003366;
	border-bottom: 2px solid #3c78b5;
}
.pagesubheading {
    color: #666666;
    font-size: 10px;
    padding: 0px 0px 5px 0px;
}

HR {
	color: 3c78b5;
	height: 1;
}

A:link, A:visited, A:active, A:hover {
	color: #003366;
}

h1 A:link, h1 A:visited, h1 A:active {
	text-decoration: none;
}

h1 A:hover {
    border-bottom: 1px dotted #003366;
}

.wiki-content > :first-child, .commentblock > :first-child {
    margin-top: 3px;
}

.logocell {
    padding: 10px;
}

input {
	font-family: verdana, geneva, arial, sans-serif;
	font-size: 11px;
	color: #000000;
}

textarea, textarea.editor {
	font-family: verdana, geneva, arial, sans-serif;
	font-size: 11px;
	color: #333333;
}

/* use logoSpaceLink instead.
.spacenametitle {
	font: 21px/31px Impact, Arial, Helvetica;
    font-weight: 100;
    color: #999999;
	margin: 0px;
}
.spacenametitle img {
  margin: 0 0 -4px 0;
}
.spacenametitle a {
    text-decoration: none;
    color: #999999;
}
.spacenametitle a:visited {
    text-decoration: none;
    color: #999999;
}*/

.spacenametitle-printable {
	font: 20px/25px Impact, Arial, Helvetica;
    font-weight: 100;
    color: #999999;
	margin: 0px;
}
.spacenametitle-printable a {
    text-decoration: none;
    color: #999999;
}
.spacenametitle-printable a:visited {
    text-decoration: none;
    color: #999999;
}

.blogDate {
	font-weight: bold;
	text-decoration: none;
	color: black;
}

.blogSurtitle {
    background: #f0f0f0;
 	border: 1px solid #ddd;
	padding: 3px;
	margin: 1px 1px 10px 1px;
}

.blogHeading {
    font-size: 20px;
    line-height: normal;
    font-weight: bold;
    padding: 0px;
    margin: 0px;
}

.blogHeading a {
   text-decoration: none;
   color: black;
}

.endsection {
	align: right;
	color: #666666;
	margin-top: 10px;
}
.endsectionleftnav {
	align: right;
	color: #666666;
	margin-top: 10px;
}

h1 {
	font-size: 24px;
	line-height: normal;
	font-weight: bold;
	background-color: #f0f0f0;
	color: #003366;
 	border-bottom: 1px solid #3c78b5;
	padding: 2px;
	margin: 36px 0px 4px 0px;
}

h2 {
	font-size: 18px;
	line-height: normal;
	font-weight: bold;
	background-color: #f0f0f0;
 	border-bottom: 1px solid #3c78b5;
	padding: 2px;
	margin: 27px 0px 4px 0px;
}

h3 {
	font-size: 14px;
	line-height: normal;
	font-weight: bold;
	background-color: #f0f0f0;
	padding: 2px;
	margin: 21px 0px 4px 0px;
}

h4 {
	font-size: 12px;
	line-height: normal;
	font-weight: bold;
	background-color: #f0f0f0;
	padding: 2px;
	margin: 18px 0px 4px 0px;
}

h4.search {
	font-size: 12px;
	line-height: normal;
	font-weight: normal;
	background-color: #f0f0f0;
	padding: 4px;
	margin: 18px 0px 4px 0px;
}

h5 {
	font-size: 10px;
	line-height: normal;
	font-weight: bold;
	background-color: #f0f0f0;
	padding: 2px;
	margin: 14px 0px 4px 0px;
}

h6 {
	font-size: 8px;
	line-height: normal;
	font-weight: bold;
	background-color: #f0f0f0;
	padding: 2px;
	margin: 14px 0px 4px 0px;
}

.smallfont {
    font-size: 10px;
}
.descfont {
    font-size: 10px;
    color: #666666;
}
.smallerfont {
    font-size: 9px;
}
.smalltext {
    color: #666666;
    font-size: 10px;
}
.smalltext a {
    color: #666666;
}
.smalltext-blue {
    color: #3c78b5;
    font-size: 10px;
}
.surtitle {
    margin-left: 1px;
    margin-bottom: 5px;
    font-size: 14px;
    color: #666666;
}

/* css hack found here:  http://www.fo3nix.pwp.blueyonder.co.uk/tutorials/css/hacks/ */
.navItemOver { font-size: 10px; font-weight: bold; color: #ffffff; background-color: #003366; cursor: hand; voice-family: '\'}\''; voice-family:inherit; cursor: pointer;}
.navItemOver a { color: #ffffff; background-color:#003366; text-decoration: none; }
.navItemOver a:visited { color: #ffffff; background-color:#003366; text-decoration: none; }
.navItemOver a:hover { color: #ffffff; background-color:#003366; text-decoration: none; }
.navItem { font-size: 10px; font-weight: bold; color: #ffffff; background-color: #3c78b5; }
.navItem a { color: #ffffff; text-decoration: none; }
.navItem a:hover { color: #ffffff; text-decoration: none; }
.navItem a:visited { color: #ffffff; text-decoration: none; }

div.padded { padding: 4px; }
div.thickPadded { padding: 10px; }
h3.macrolibrariestitle {
    margin: 0px 0px 0px 0px;
}

div.centered { text-align: center; margin: 10px; }
div.centered table {margin: 0px auto; text-align: left; }

.tableview table {
    margin: 0;
}

.tableview th {
    text-align: left;
    color: #003366;
    font-size: 12px;
    padding: 5px 0px 0px 5px;
    border-bottom: 2px solid #3c78b5;
}
.tableview td {
    text-align: left;
    border-color: #ccc;
    border-width: 0px 0px 1px 0px;
    border-style: solid;
    margin: 0;
    padding: 4px 10px 4px 5px;
}

.grid {
    margin: 2px 0px 5px 0px;
    border-collapse: collapse;
}
.grid th  {
    border: 1px solid #ccc;
    padding: 2px 4px 2px 4px;
    background: #f0f0f0;
    text-align: center;
}
.grid td  {
    border: 1px solid #ccc;
    padding: 3px 4px 3px 4px;
}
.gridHover {
	background-color: #f9f9f9;
}

td.infocell {
    background-color: #f0f0f0;
}
.label {
	font-weight: bold;
	color: #003366;
}

label {
	font-weight: bold;
	color: #003366;
}

.error {
	background-color: #fcc;
}

.errorBox {
	background-color: #fcc;
    border: 1px solid #c00;
    padding: 5px;
    margin: 5px;
}

.errorMessage {
	color: #c00;
}

.success {
	background-color: #dfd;
}

.successBox {
	background-color: #dfd;
    border: 1px solid #090;
    padding: 5px;
    margin-top:5px;
    margin-bottom:5px;
}

blockquote {
	padding-left: 10px;
	padding-right: 10px;
	margin-left: 5px;
	margin-right: 0px;
	border-left: 1px solid #3c78b5;
}

table.confluenceTable
{
    margin: 5px;
    border-collapse: collapse;
}

/* Added as a temporary fix for CONF-4223. The table elements appear to be inheriting the border: none attribute from the sectionMacro class */
table.confluenceTable td.confluenceTd
{
    border-width: 1px;
    border-style: solid;
    border-color: #ccc;
    padding: 3px 4px 3px 4px;
}

/* Added as a temporary fix for CONF-4223. The table elements appear to be inheriting the border: none attribute from the sectionMacro class */
table.confluenceTable th.confluenceTh
{
    border-width: 1px;
    border-style: solid;
    border-color: #ccc;
    padding: 3px 4px 3px 4px;
    background-color: #f0f0f0;
    text-align: center;
}

td.confluenceTd
{
    border-width: 1px;
    border-style: solid;
    border-color: #ccc;
    padding: 3px 4px 3px 4px;
}

th.confluenceTh
{
    border-width: 1px;
    border-style: solid;
    border-color: #ccc;
    padding: 3px 4px 3px 4px;
    background-color: #f0f0f0;
    text-align: center;
}

DIV.small {
	font-size: 9px;
}

H1.pagename {
	margin-top: 0px;
}

IMG.inline  {}

.loginform {
    margin: 5px;
    border: 1px solid #ccc;
}

/* The text how the "This is a preview" comment should be shown. */
.previewnote { text-align: center;
                font-size: 11px;
                    color: red; }

/* How the preview content should be shown */
.previewcontent { background: #E0E0E0; }

/* How the system messages should be shown (DisplayMessage.jsp) */
.messagecontent { background: #E0E0E0; }

/* How the "This page has been modified..." -comment should be shown. */
.conflictnote { }

.createlink {
    color: maroon;
}
a.createlink {
    color: maroon;
}
.templateparameter {
    font-size: 9px;
    color: darkblue;
}

.diffadded {
    background: #ddffdd;
    padding: 1px 1px 1px 4px;
	border-left: 4px solid darkgreen;
}
.diffdeleted {
    color: #999;
    background: #ffdddd;
    padding: 1px 1px 1px 4px;
	border-left: 4px solid darkred;
}
.diffnochange {
    padding: 1px 1px 1px 4px;
	border-left: 4px solid lightgrey;
}
.differror {
    background: brown;
}
.diff {
    font-family: lucida console, courier new, fixed-width;
	font-size: 12px;
	line-height: 14px;
}
.diffaddedchars {
    background-color:#99ff99;
    font-weight:bolder;
}
.diffremovedchars {
    background-color:#ff9999;
    text-decoration: line-through;
    font-weight:bolder;
}

.greybackground {
    background: #f0f0f0
}

.greybox {
 	border: 1px solid #ddd;
	padding: 3px;
	margin: 1px 1px 10px 1px;
}

.borderedGreyBox {
    border: 1px solid #cccccc;
    background-color: #f0f0f0;
    padding: 10px;
}

.greyboxfilled {
 	border: 1px solid #ddd;
    background: #f0f0f0;
    padding: 3px;
	margin: 1px 1px 10px 1px;
}

.navBackgroundBox {
    padding: 5px 5px 5px 5px;
    font-size: 22px;
	font-weight: bold;
	font-family: Arial, sans-serif;
	color: white;
    background: #3c78b5;
    text-decoration: none;
}

.previewBoxTop {
	background-color: #f0f0f0;
    border-width: 1px 1px 0px 1px;
    border-style: solid;
    border-color: #3c78b5;
    padding: 5px;
    margin: 5px 0px 0px 0px;
    text-align: center;
}
.previewContent {
    background-color: #fff;
 	border-color: #3c78b5;
	border-width: 0px 1px 0px 1px;
	border-style: solid;
	padding: 10px;
	margin: 0px;
}
.previewBoxBottom {
	background-color: #f0f0f0;
    border-width: 0px 1px 1px 1px;
    border-style: solid;
    border-color: #3c78b5;
    padding: 5px;
    margin: 0px 0px 5px 0px;
    text-align: center;
}

.functionbox {
    background-color: #f0f0f0;
 	border: 1px solid #3c78b5;
	padding: 3px;
	margin: 1px 1px 10px 1px;
}

.functionbox-greyborder {
    background-color: #f0f0f0;
 	border: 1px solid #ddd;
	padding: 3px;
	margin: 1px 1px 10px 1px;
}

.search-highlight {
    background-color: #ffffcc;
}

/* normal (white) background */
.rowNormal {
    background-color: #ffffff;
 }

/* alternate (pale yellow) background */
.rowAlternate {
    background-color: #f7f7f7;
}

/* used in the list attachments table */
.rowAlternateNoBottomColor {
    background-color: #f7f7f7;
}

.rowAlternateNoBottomNoColor {
}

.rowAlternateNoBottomColor td {
    border-bottom: 0px;
}

.rowAlternateNoBottomNoColor td {
    border-bottom: 0px;
}

/* row highlight (grey) background */
.rowHighlight {
    background-color: #f0f0f0;

}

TD.greenbar {FONT-SIZE: 2px; BACKGROUND: #00df00; BORDER: 1px solid #9c9c9c; PADDING: 0px; }
TD.redbar {FONT-SIZE: 2px; BACKGROUND: #df0000; BORDER: 1px solid #9c9c9c; PADDING: 0px; }
TD.darkredbar {FONT-SIZE: 2px; BACKGROUND: #af0000; BORDER: 1px solid #9c9c9c; PADDING: 0px; }

TR.testpassed {FONT-SIZE: 2px; BACKGROUND: #ddffdd; PADDING: 0px; }
TR.testfailed {FONT-SIZE: 2px; BACKGROUND: #ffdddd; PADDING: 0px; }

.toolbar  {
    margin: 0px;
    border-collapse: collapse;
}

.toolbar td  {
    border: 1px solid #ccc;
    padding: 2px 2px 2px 2px;
    color: #ccc;
}

td.noformatting {
    border-width: 0px;
    border-style: none;
    text-align: center;
	padding: 0px;
}

.commentblock {
    margin: 12px 0 12px 0;
}

/*
 * Divs displaying the license information, if necessary.
 */
.license-eval, .license-none, .license-nonprofit {
    border-top: 1px solid #bbbbbb;
    text-align: center;
    font-size: 10px;
    font-family: Verdana, Arial, Helvetica, sans-serif;
}

.license-eval, .license-none {
    background-color: #ffcccc;
}

.license-eval b, .license-none b {
    color: #990000
}

.license-nonprofit {
    background-color: #ffffff;
}

/*
 * The shadow at the bottom of the page between the main content and the
 * "powered by" section.
 */
.bottomshadow {
    height: 12px;
    background-image: url("$req.contextPath/images/border/border_bottom.gif");
    background-repeat: repeat-x;
}

/*
 * Styling of the operations box
 */
.navmenu .operations li, .navmenu .operations ul {
    list-style: none;
    margin-left: 0;
    padding-left: 0;
}

.navmenu .operations ul {
    margin-bottom: 9px;
}

.navmenu .label {
    font-weight: inherit;
}

/*
 * Styling of ops as a toolbar
 */
.toolbar div {
    display: none;
}

.toolbar .label {
    display: none;
}

.toolbar .operations {
    display: block;
}

.toolbar .operations ul {
    display: inline;
    list-style: none;
    margin-left: 10px;
    padding-left: 0;
}

.toolbar .operations li {
    list-style: none;
    display: inline;
}

/* list page navigational tabs */
#foldertab {
padding: 3px 0px 3px 8px;
margin-left: 0;
border-bottom: 1px solid #3c78b5;
font: bold 11px Verdana, sans-serif;
}

#foldertab li {
list-style: none;
margin: 0;
display: inline;
}

#foldertab li a {
padding: 3px 0.5em;
margin-left: 3px;
border: 1px solid #3c78b5;
border-bottom: none;
background: #3c78b5;
text-decoration: none;
}

#foldertab li a:link { color: #ffffff; }
#foldertab li a:visited { color: #ffffff; }

#foldertab li a:hover {
color: #ffffff;
background: #003366;
border-color: #003366;
}

#foldertab li a.current {
background: white;
border-bottom: 1px solid white;
color: black;
}

#foldertab li a.current:link { color: black; }
#foldertab li a.current:visited { color: black; }
#foldertab li a.current:hover {
background: white;
border-bottom: 1px solid white;
color: black;
}

/* alphabet list */
ul#squaretab {
margin-left: 0;
padding-left: 0;
white-space: nowrap;
font: bold 8px Verdana, sans-serif;
}

#squaretab li {
display: inline;
list-style-type: none;
}

#squaretab a {
padding: 2px 6px;
border: 1px solid #3c78b5;
}

#squaretab a:link, #squaretab a:visited {
color: #fff;
background-color: #3c78b5;
text-decoration: none;
}

#squaretab a:hover {
color: #ffffff;
background-color: #003366;
border-color: #003366;
text-decoration: none;
}

#squaretab li a#current {
background: white;
color: black;
}

.blogcalendar * {
    font-family:verdana, arial, sans-serif;
    font-size:x-small;
    font-weight:normal;
    line-height:140%;
    padding:2px;
}


table.blogcalendar {
    border: 1px solid #3c78b5;
}

.blogcalendar th.calendarhead, a.calendarhead {
    font-size:x-small;
    font-weight:bold;
    padding:2px;
    text-transform:uppercase;
    background-color: #3c78b5;
    color: #ffffff;
    letter-spacing: .3em;
    text-transform: uppercase;
}

.calendarhead:visited {color: white;}
.calendarhead:active {color: white;}
.calendarhead:hover {color: white;}

.blogcalendar th {
    font-size:x-small;
    font-weight:bold;
    padding:2px;
    background-color:#f0f0f0;
}

.blogcalendar td {
    font-size:x-small;
    font-weight:normal;
}

.searchGroup { padding: 0 0 10px 0; background: #f0f0f0; }
.searchGroupHeading { font-size: 10px; font-weight: bold; color: #ffffff; background-color: #3c78b5; padding: 2px 4px 1px 4px; }
.searchItem { padding: 1px 4px 1px 4px; }
.searchItemSelected { padding: 1px 4px 1px 4px; font-weight: bold; background: #ddd; }

/* permissions page styles */
.permissionHeading {
    border-bottom: #bbb; border-width: 0 0 1px 0; border-style: solid; font-size: 16px; text-align: left;
}
.permissionTab {
    border-width: 0 0 0 1px; border-style: solid; background: #3c78b5; color: #ffffff; font-size: 10px;
}
.permissionSuperTab {
    border-width: 0 0 0 1px; border-style: solid; background: #003366; color: #ffffff;
}
.permissionCell {
    border-left: #bbb; border-width: 0 0 0 1px; border-style: solid;
}

/* warning panel */
.warningPanel { background: #FFFFCE; border:#F0C000 1px solid; padding: 8px; margin: 10px; }
/* alert panel */
.alertPanel { background: #FFCCCC; border:#C00 1px solid; padding: 8px; margin: 10px; }
/* info panel */
.infoPanel { background: #D8E4F1; border:#3c78b5 1px solid; padding: 8px; margin: 10px; }

/* side menu highlighting (e.g. space content screen) */
.optionPadded { padding: 2px; }
.optionSelected { background-color: #ffffcc; padding: 2px; border: 1px solid #ddd; margin: -1px; }
.optionSelected a { font-weight: bold; text-decoration: none; color: black; }

/* information macros */
.noteMacro { border-style: solid; border-width: 1px; border-color: #F0C000; background-color: #FFFFCE; text-align:left; margin-top: 5px; margin-bottom: 5px}
.warningMacro { border-style: solid; border-width: 1px; border-color: #c00; background-color: #fcc; text-align:left; margin-top: 5px; margin-bottom: 5px}
.infoMacro { border-style: solid; border-width: 1px; border-color: #3c78b5; background-color: #D8E4F1; text-align:left; margin-top: 5px; margin-bottom: 5px}
.tipMacro { border-style: solid; border-width: 1px; border-color: #090; background-color: #dfd; text-align:left; margin-top: 5px; margin-bottom: 5px}
.informationMacroPadding { padding: 5px 0 0 5px; }

table.infoMacro td, table.warningMacro td, table.tipMacro td, table.noteMacro td, table.sectionMacro td {
    border: none;
}

table.sectionMacroWithBorder td.columnMacro { border-style: dashed; border-width: 1px; border-color: #cccccc;}

.pagecontent
{
    padding: 10px;
    text-align: left;
}

/* styles for links in the top bar */
.topBarDiv a:link {color: #ffffff;}
.topBarDiv a:visited {color: #ffffff;}
.topBarDiv a:active {color: #ffffff;}
.topBarDiv a:hover {color: #ffffff;}
.topBarDiv {color: #ffffff;}

.topBar {
    background-color: #003366;
}


/* styles for extended operations */
.greyLinks a:link {color: #666666; text-decoration:underline;}
.greyLinks a:visited {color: #666666; text-decoration:underline;}
.greyLinks a:active {color: #666666; text-decoration:underline;}
.greyLinks a:hover {color: #666666; text-decoration:underline;}
.greyLinks {color: #666666; display:block; padding: 10px}

.logoSpaceLink {color: #999999; text-decoration: none}
.logoSpaceLink a:link {color: #999999; text-decoration: none}
.logoSpaceLink a:visited {color: #999999; text-decoration: none}
.logoSpaceLink a:active {color: #999999; text-decoration: none}
.logoSpaceLink a:hover {color: #003366; text-decoration: none}

/* basic panel (basicpanel.vmd) style */
.basicPanelContainer {border: 1px solid #3c78b5; margin-top: 2px; margin-bottom: 8px; width: 100%}
.basicPanelTitle {padding: 5px; margin: 0px; background-color: #f0f0f0; color: black; font-weight: bold;}
.basicPanelBody {padding: 5px; margin: 0px}

.separatorLinks a:link {color: white}
.separatorLinks a:visited {color: white}
.separatorLinks a:active {color: white}

.greynavbar {background-color: #f0f0f0; border-top: 1px solid #3c78b5; margin-top: 2px}

div.headerField {
    float: left;
    width: auto;
    height: 100%;
}

.headerFloat {
    margin-left: auto;
    width: 50%;
}

.headerFloatLeft {
    float: left;
    margin-right: 20px;
    margin-bottom: 10px;
}

#headerRow {
    padding: 10px;
}

div.license-personal {
   background-color: #003366;
   color: #ffffff;
}

div.license-personal a {
   color: #ffffff;
}

.greyFormBox {
    border: 1px solid #cccccc;
    padding: 5px;
}

/* IE automatically adds a margin before and after form tags. Use this style to remove that */
.marginlessForm {
    margin: 0px;
}

.openPageHighlight {
    background-color: #ffffcc;
    padding: 2px;
    border: 1px solid #ddd;
}

.editPageInsertLinks, .editPageInsertLinks a
{
    color: #666666;
    font-weight: bold;
    font-size: 10px;
}

/* Style for label heatmap. */
.top10 a {
    font-weight: bold;
    font-size: 2em;
    color: #003366;
}
.top25 a {
    font-weight: bold;
    font-size: 1.6em;
    color: #003366;
}
.top50 a {
    font-size: 1.4em;
    color: #003366;
}
.top100 a {
    font-size: 1.2em;
    color: #003366;
}

.heatmap {
    list-style:none;
    width: 95%;
    margin: 0px auto;
}

.heatmap a {
    text-decoration:none;
}

.heatmap a:hover {
    text-decoration:underline;
}

.heatmap li {
    display: inline;
}

.minitab {
padding: 3px 0px 3px 8px;
margin-left: 0;
margin-top: 1px;
margin-bottom: 0px;
border-bottom: 1px solid #3c78b5;
font: bold 9px Verdana, sans-serif;
text-decoration: none;
float:none;
}
.selectedminitab {
padding: 3px 0.5em;
margin-left: 3px;
margin-top: 1px;
border: 1px solid #3c78b5;
background: white;
border-bottom: 1px solid white;
color: #000000;
text-decoration: none;
}
.unselectedminitab {
padding: 3px 0.5em;
margin-left: 3px;
margin-top: 1px;
border: 1px solid #3c78b5;
border-bottom: none;
background: #3c78b5;
color: #ffffff;
text-decoration: none;
}

a.unselectedminitab:hover {
color: #ffffff;
background: #003366;
border-color: #003366;
}

a.unselectedminitab:link { color: white; }
a.unselectedminitab:visited { color: white; }

a.selectedminitab:link { color: black; }
a.selectedminitab:visited { color: black; }

.linkerror { background-color: #fcc;}

a.labelOperationLink:link {text-decoration: underline}
a.labelOperationLink:active {text-decoration: underline}
a.labelOperationLink:visited {text-decoration: underline}
a.labelOperationLink:hover {text-decoration: underline}

a.newLabel:link {background-color: #ddffdd}
a.newLabel:active {background-color: #ddffdd}
a.newLabel:visited {background-color: #ddffdd}
a.newLabel:hover {background-color: #ddffdd}

ul.square {list-style-type: square}

.inline-control-link {
    background: #ffc;
    font-size: 9px;
    color: #666;
    padding: 2px;
    text-transform: uppercase;
    text-decoration: none;
}


.inline-control-link a:link {text-decoration: none}
.inline-control-link a:active {text-decoration: none}
.inline-control-link a:visited {text-decoration: none}
.inline-control-link a:hover {text-decoration: none}

.inline-control-link {
    background: #ffc;
    font-size: 9px;
    color: #666;
    padding: 2px;
    text-transform: uppercase;
    text-decoration: none;
    cursor: pointer;
}

div.auto_complete {
    width: 350px;
    background: #fff;
}
div.auto_complete ul {
    border: 1px solid #888;
    margin: 0;
    padding: 0;
    width: 100%;
    list-style-type: none;
}
div.auto_complete ul li {
    margin: 0;
    padding: 3px;
}
div.auto_complete ul li.selected {
    background-color: #ffb;
}
div.auto_complete ul strong.highlight {
    color: #800;
    margin: 0;
    padding: 0;
}

/******* Edit Page Styles *******/
.toogleFormDiv{
    border:1px solid #A7A6AA;
    background-color:white;
    padding:5px;
    margin-top: 5px;
}

.toogleInfoDiv{
    border:1px solid #A7A6AA;
    background-color:white;
    display:none;
    padding:5px;
    margin-top: 10px;
}

.inputSection{
    margin-bottom:20px;
}

#editBox{
   border:1px solid lightgray;
   background-color:#F0F0F0;
}

/******* Left Navigation Theme Styles ********/
.leftnav li a {
    text-decoration:none;
    color:white;
    margin:0px;
    display:block;
    padding:2px;
    padding-left:5px;
    background-color: #3c78b5;
    border-top:1px solid #3c78b5;
}

.leftnav li a:active {color:white;}
.leftnav li a:visited {color:white;}
.leftnav li a:hover {background-color: #003366; color:white;}

/* Added by Shaun during i18n */
.replaced
{
    background-color: #33CC66;
}

.topPadding
{
    margin-top: 20px;
}

/* new form style */
.form-block {
    padding: 6px;
}
.form-error-block {
    padding: 6px;
    background: #fcc;
    border-top: #f0f0f0 1px solid;
    border-bottom: #f0f0f0 1px solid;
    margin-bottom: 6px;
    padding: 0 12px 0 12px;
}
.form-element-large {
    font-size: 16px;
    font-weight: bold;
    font-family: Arial, sans-serif;
    color: #003366;
}

.form-element-small {
    font-size: 12px;
    font-weight: bold;
    font-family: Arial, sans-serif;
    color: #003366;
}

.form-header {
    background: lightyellow;
    border-top: #f0f0f0 1px solid;
    border-bottom: #f0f0f0 1px solid;
    margin-bottom: 6px;
    padding: 0 12px 0 12px;
}
.form-header p, .form-block p, .form-error-block p {
    line-height: normal;
    margin: 12px 0 12px 0;
}
.form-example {
    color: #888;
    font-size: 11px;
}
.form-divider {
    border-bottom: #ccc 1px solid;
    margin-bottom: 6px;
}
.form-buttons {
    margin-top: 6px;
    border-top: #ccc 1px solid;
    border-bottom: #ccc 1px solid;
    background: #f0f0f0;
    padding: 10px;
    text-align: center;
}
.form-buttons input {
    width: 100px;
}
.form-block .error {
    padding: 6px;
    margin-bottom: 6px;
}
    -->
    </style>
</head>
<body>

<div id="PageContent">
<table class="pagecontent" border="0" cellpadding="0" cellspacing="0" width="100%"><tr>
<td valign="top" class="pagebody">

    <div class="pageheader">
        <span class="pagetitle">
            Page Edited :
            <a href="http://cwiki.apache.org/confluence/display/ODExSITE">ODExSITE</a> :
            <a href="http://cwiki.apache.org/confluence/display/ODExSITE/Jacob">Jacob</a>
        </span>
    </div>

     <p>
        <a href="http://cwiki.apache.org/confluence/display/ODExSITE/Jacob">Jacob</a>
        has been edited by             <a href="http://cwiki.apache.org/confluence/display/~mriou">Matthieu Riou</a>
            <span class="smallfont">(Oct 05, 2007)</span>.
     </p>
    <p>
      Change summary:
      <div class="greybox wiki-content"><p>Replace all on incubator broken links</p></div>
    </p>
     <p>
                 <a href="http://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=27392&originalVersion=2&revisedVersion=3">(View changes)</a>
     </p>

    <span class="label">Content:</span><br/>
    <div class="greybox wiki-content"><h3><a name="Jacob-Introduction"></a>Introduction</h3>

<p>Ode's BPEL implementation relies on the JACOB framework to implement the BPEL constructs. The framework provides the mechanism necessary to deal with two key issues in implementing BPEL constructs:</p>

<ol>
	<li>Persistence of execution state.</li>
	<li>Concurrency.</li>
</ol>


<p>By rolling up these concerns in the framework, the implementation of the BPEL constructs can be simpler by limiting itself to implementing the BPEL logic and not the infrastructure necessary to support it.</p>

<p>The approach we'll take in this tutorial is looking at the <a href="#Jacob-rational" title="rational on Jacob">rational</a> of Jacob and its <a href="#Jacob-concepts" title="concepts on Jacob">concepts</a> first. Then we'll illustrate with one complete <a href="#Jacob-examples" title="examples on Jacob">example</a>. But if you're a reverse reader, you can also decide to start with the <a href="#Jacob-example1" title="example1 on Jacob">example</a>.</p>

<h3><a name="Jacob-Rationalebehindthemodel"></a>Rationale behind the model</h3>

<p>Let's start from the most classical example of all:</p>

<div class="code"><div class="codeContent">
<pre class="code-java">void process(order) {
  billingService.bill(order.billing);
  shippingService.ship(order.product, order.shipping, self);
  shipping = receive(<span class="code-quote">"shipping"</span>)
  order.customer.send(shipping.details);
}</pre>
</div></div>

<p>It's Java like pseudo code that works extremely well, unless:</p>

<ol>
	<li>You fail somewhere in the middle and the customer has to order a second time.</li>
	<li>Your have too many waiting threads and the VM crashes.</li>
</ol>


<p>So you change it to:</p>

<div class="code"><div class="codeContent">
<pre class="code-java">void process(order) {
  billingService.bill(order.billing);
  shippingService.ship(order.product, order.shipping, self);
  listenOn(<span class="code-quote">"shipping"</span>, part2);
}

void part2(shipping) {
  order.customer.send(shipping.details);
}</pre>
</div></div>

<p>That's almost better, but still has a lot of points of failure, where you're not sure if you actually billed the customer and shipped the product or not. So:</p>

<div class="code"><div class="codeContent">
<pre class="code-java">void process(order) {
  billingService.bill(order.billing);
  <span class="code-keyword">continue</span>(<span class="code-quote">"part2"</span>);
}

void part2() {
  shippingService.ship(order.product, order.shipping, self);
  listenOn(<span class="code-quote">"shipping"</span>, part2);
}

void part2(shipping) {
  order.customer.send(shipping.details);
}</pre>
</div></div>

<p>You're just fracturing the code with two primitives: <em>continue</em> which lets you persist state and go to the next one, and <em>listenOn</em> which lets you persist state and wait for an external event.</p>

<p>Fracturing the code also addresses concurrency of execution even if you only have one thread. For example you could have a large number of 'process' calls to bill and ship an order. As we've broken down the whole treatment into several small parts, we can control when these separate parts actually get called and executed. And we're free to order them.</p>

<p>Let's say we have a small process doing in a sequence:</p>
<div class="code"><div class="codeContent">
<pre class="code-java">1. Invoke
2. Receive
3. Wait
4. Invoke</pre>
</div></div>
<p>If we have 2 parrallel executions of this process and implement it in a simple way we'd have:</p>
<div class="code"><div class="codeContent">
<pre class="code-java">1. Invoke1
2. Receive1
3. Wait1
4. Invoke1
5. Invoke2
6. Receive2
7. Wait2
8. Invoke2</pre>
</div></div>
<p>However if we break down the code as shown above by introducing a "middle man" (or stack) and do not allow activities to directly call each other (or do not have the activity directly calling the sequence that directly calls the next activity) we could well obtain the following:</p>
<div class="code"><div class="codeContent">
<pre class="code-java">1. Invoke1
5. Invoke2
2. Receive1
3. Wait1
6. Receive2
7. Wait2
4. Invoke1
8. Invoke2</pre>
</div></div>
<p>From a client standpoint, we've achieved concurrency of execution even with one thread.</p>

<p>Next step is adding links, fault handling/termination, compensation and event handlers and seeing that continue/listenOn is all you need. The last step is just adding implementation details.</p>

<h3><a name="Jacob-JacobExample"></a>Jacob Example</h3>

<p>Consider the issue of persistence. Imagine a simplified and naive implementation of the BPEL constructs &lt;sequence&gt;, &lt;wait&gt;, and &lt;empty&gt;:</p>

<div class="code"><div class="codeContent">
<pre class="code-java">class Sequence <span class="code-keyword">extends</span> Activity {
  /** From BPEL Definition */
  OSequence def;
  ...
  void run() {
     <span class="code-keyword">for</span> (OActivity child:def.children)
       createActivity(child).run();
  }
}

class Wait <span class="code-keyword">extends</span> Activity {
  /** From BPEL Definition */
  OWait def;
  ...
  void run() {
     <span class="code-object">Thread</span>.wait(def.duration);
  }
}

class Empty <span class="code-keyword">extends</span> Activity {
  /** From BPEL Definition */
  OEmpty def;
  ...
  void run() {
     <span class="code-comment">// &lt;empty&gt; activity: <span class="code-keyword">do</span> nothing.
</span>  }
}</pre>
</div></div>

<p>The above is the "natural" implementation of the constructs in the Java language. However, this implementation has some serious and obvious problems: the &lt;wait&gt; may specify a duration of days in which case a system failure during those days of waiting would mean that the process execution state would be lost. It would be nice to replace Thread.wait() in the Wait class with some sort of "suspend" operator that would save the execution state to disk.</p>

<p>However there are practical issues to "suspending" to disk. At the point we wish to suspend, the call stack looks like:</p>
<div class="code"><div class="codeContent">
<pre class="code-java">Sequence.run()
Wait.run()</pre>
</div></div>

<p>In order to save the process to disk, we need to end the current thread of control which means popping both stack frames. To do this we have no choice but to require the implementation of Wait and Sequence to "cooperate" with this requirement thereby greatly complicating the implementation of those constructs. This also means that the "natural" implementation model cannot be used directly.</p>

<p>JACOB aims to solve this problem by providing an alternate "natural" model that allows execution state to be suspended ''without cooperation from the implementation classes''. The idea in JACOB is to flatten the call stack and rely on explicit communication channels to handle control flow. We now consider a simplified JACOB representation of our three BPEL activities:</p>

<div class="code"><div class="codeContent">
<pre class="code-java">class Empty  {
  OEmpty def;

  /** channel we use to notify parent we are done. */
  CompletionChannel myCompletionChannel;
  ...
  void run() {
     <span class="code-comment">// &lt;empty&gt; activity: <span class="code-keyword">do</span> nothing...except to
</span>     <span class="code-comment">// notify our parent that we are done.
</span>     myCompletionChannel.completed();
  }
}

class Sequence  {
  OSequence def;
  CompletionChannel myCompletionChannel;
  ...
  void run() {
    <span class="code-comment">// Start of by instantiating a sequential child runner <span class="code-keyword">for</span> the first
</span>    <span class="code-comment">// (0th) child...
</span>    instance(<span class="code-keyword">new</span> SequenceChildRunner(0))
  }

  class SequenceChildRunner {
    <span class="code-object">int</span> currentchild;
    SequenceChildRunner(<span class="code-object">int</span> childNumber) { currentchild = childNumber; }
    void run() {
       <span class="code-keyword">if</span>(currentChild == def.children.size()) {
         <span class="code-comment">// We are past the last child, the sequence is done.
</span>         myCompletionChannel.completed();
       } <span class="code-keyword">else</span> {
         <span class="code-comment">// We still have more children to run..
</span>  
         <span class="code-comment">// Create a completion channel <span class="code-keyword">for</span> our child.
</span>         CompletionChannel childCompletionChannel = 
            newChannel(CompletionChannel.class);

         <span class="code-comment">// create a child activity based on the activity type
</span>         <span class="code-comment">// and parameterized with the model and the completion
</span>         <span class="code-comment">// channel we just created
</span>         Activity childActivity = 
            createActivity(def.children.get(currentChild),
                           childCompletionChannel);

         <span class="code-comment">// instantiate the child activity
</span>         instance(childActivity);

         <span class="code-comment">// create an object to wait <span class="code-keyword">for</span> the <span class="code-quote">"completed()"</span> notification
</span>         <span class="code-comment">// from the child activity.
</span>         object(<span class="code-keyword">new</span> CompletionChannelML(childCompletionChannel)) {
            void completed() {
               <span class="code-comment">// Ok, finished with the child, create a runner
</span>               <span class="code-comment">// to <span class="code-keyword">do</span> the next child.
</span>               instance(<span class="code-keyword">new</span> SequenceChildRunner(currentChild+1));
            }
         }
       }
    }
  }
}

class Wait <span class="code-keyword">extends</span> Activity {
  OWait def;
  CompletionChannel myCompletionChannel;
   
  ...
  void run() {
     <span class="code-comment">// Create a channel <span class="code-keyword">for</span> an externally managed alarm.
</span>     TimerChannel timerChannel = newChannel(TimerChannel.class);
     <span class="code-comment">// register the alarm with the runtime.
</span>     getRuntimeContext().registerTimer(timerChannel, def.duration);

     <span class="code-comment">// create an object to wait <span class="code-keyword">for</span> the alarm and then send an
</span>     <span class="code-comment">// activity completed message to our parent.
</span>     object(<span class="code-keyword">new</span> TimerChannelML(timerChannel) {
        onTimer() {
           myCompletionChannel.completed();
        }
     });
   
  }
}</pre>
</div></div>

<p>So Jacob constructs help us in breaking the execution stack.</p>

<h3><a name="Jacob-MainJacobConcepts%3D"></a>Main Jacob Concepts =</h3>

<h4><a name="Jacob-Channels"></a>Channels</h4>

<p>As briefly demonstrated above, channels are interfaces used for communication between activities in PXE engine. There are several types of channels like TerminationChannel, ParentScopeChannel or CompensationChannel (their respective purpose should be obvious from their name). Some basic channels are provided to all activities when they're created to allow them to interact with their environment. When an activity wants to notifies its parent that it has terminated for example, it just calls its parent TerminationChannel (see the Empty example above).</p>

<p>Don't look for channels implementations because there are none. Channels implementation is provided through a dynamic proxy (see <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/jacob/src/main/java/org/apache/ode/jacob/vpu/ChannelFactory.java" title="Visit page outside Confluence" rel="nofollow">ChannelFactory<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span>.createChannel() and <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/jacob/src/main/java/org/apache/ode/jacob/vpu/ChannelFactory.java" title="Visit page outside Confluence" rel="nofollow">ChannelFactory<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span>.ChannelInvocationHandler for more). That's one of the levels of decoupling between invocation and actual execution in Jacob.</p>

<h4><a name="Jacob-JacobObject%2FJacobRunnable"></a>JacobObject / JacobRunnable</h4>

<p>If you don't care much about the details, the bottom line is: a <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/jacob/src/main/java/org/apache/ode/jacob/JacobObject.java" title="Visit page outside Confluence" rel="nofollow">JacobObject<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span> and an <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/jacob/src/main/java/org/apache/ode/jacob/JacobRunnable.java" title="Visit page outside Confluence" rel="nofollow">JacobRunnable<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span> are just a method implementation. This method gets executed when the abstraction is executed.</p>

<p>A JacobObject is meant to be a closure. From Wikipedia: "A closure combines the code of a function with a special lexical environment bound to that function (scope). Closure lexical variables differ from global variables in that they do not occupy the global variable namespace. They differ from object oriented object variables in that they are bound to functions, not objects.". Normally closures aren't supported in Java so <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/jacob/src/main/java/org/apache/ode/jacob/JacobObject.java" title="Visit page outside Confluence" rel="nofollow">JacobObject<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span> tries to feel that gap. But it's not a true closure anyway, which makes thing easier. Closures in Jacob are statically coded, whereas in most languages supporting closures these are dynamic. So basically in Jacob, a closure is expected to implement some methods and provides other utility methods to manipulate channels and replicate itself.</p>

<p><span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/jacob/src/main/java/org/apache/ode/jacob/JacobRunnable.java" title="Visit page outside Confluence" rel="nofollow">JacobRunnable<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span> is just a JacobObject that requires the implementation of only one method: run(). As ''all activities inherit from <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/jacob/src/main/java/org/apache/ode/jacob/JacobRunnable.java" title="Visit page outside Confluence" rel="nofollow">JacobRunnable<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span> they're all supposed to implement their main processing in this run() method. Their initialization occur in their respective constructors.</p>

<h4><a name="Jacob-MethodLists%28MLs%29"></a>Method Lists (MLs)</h4>

<p>ML classes can be seen as the other end of a channel. Only they're not invoked directly when one calls a channel method, but only once the Jacob engine has popped the channel invocation from its internal stack (again you can see how the execution stack gets broken here).</p>

<p>Usually MLs implementations in PXE are inlined because it's just easier to declare them in the activities run() method. For example if you look at the Sequence example shown above you'll see something like:</p>

<div class="code"><div class="codeContent">
<pre class="code-java">void run() {
	...
         <span class="code-comment">// create an object to wait <span class="code-keyword">for</span> the <span class="code-quote">"completed()"</span> notification
</span>         <span class="code-comment">// from the child activity.
</span>         object(<span class="code-keyword">new</span> CompletionChannelML(childCompletionChannel)) {
            void completed() {
               <span class="code-comment">// Ok, finished with the child, create a runner
</span>               <span class="code-comment">// to <span class="code-keyword">do</span> the next child.
</span>               instance(<span class="code-keyword">new</span> SequenceChildRunner(currentChild+1));
            }
         }
       }
    }</pre>
</div></div>

<p>The object method here is inherited from !JacobObject and is just a way to hand our ML to Jacob. So that the Jacob runtime can match it with an incoming channel message later on.</p>

<h4><a name="Jacob-VPUandExecutionQueue"></a>VPU and ExecutionQueue</h4>

<p>The <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/jacob/src/main/java/org/apache/ode/jacob/vpu/JacobVPU.java" title="Visit page outside Confluence" rel="nofollow">VPU<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span> is where all the Jacob processing is occuring. When a JacobObject is injected inside the VPU, it's actually registered as a <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/jacob/src/main/java/org/apache/ode/jacob/soup/Continuation.java" title="Visit page outside Confluence" rel="nofollow">Continuation<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span>, which is just wrapping the JacobObject with the method to call on the JacobObject to execute it (in our case always the run() method as we're only dealing with JacobRunnable instances).</p>

<p>The <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/jacob/src/main/java/org/apache/ode/jacob/soup/ExecutionQueue.java" title="Visit page outside Confluence" rel="nofollow">ExecutionQueue<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span> (and its implementation <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/jacob/src/main/java/org/apache/ode/jacob/vpu/FastExecutionQueueImpl.java" title="Visit page outside Confluence" rel="nofollow">FastExecutionQueueImpl<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span>) is just a container for all the artifacts managed by the VPU (mostly channels and reactions) to organize them in queues where artifacts can be pushed and popped. It also records some execution statistics.</p>

<p>So the VPU main processing is just dequeuing a reaction from the soup and executing it by calling its abstraction's run() method (remember that the reaction just wraps an abstraction). That's all (check <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/jacob/src/main/java/org/apache/ode/jacob/vpu/JacobVPU.java" title="Visit page outside Confluence" rel="nofollow">JacobVPU<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span>.execute(), you'll see that I'm not lying). However when the JacobRunnable (usually an activity) gets executed the following things can happen:</p>

<ul>
	<li>if other abstractions (usually other activities) are created, they will be appended to the reaction queue,</li>
	<li>if new channels are created, they will be saved for later usage,</li>
	<li>if channels are invoked, the message will be saved to match against a new ML,</li>
	<li>if a new ML instance is created, it will be submitted to the VPU that will try to match it against a channel invocation.</li>
</ul>


<p>The VPU is also responsible for persisting its internal state. So when an execution stops (for example our process has reach a receive) the VPU state is serialized and saved for later reuse. This logic can be seen in <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/bpel-runtime/src/main/java/org/apache/ode/bpel/runtime/RuntimeContextImpl" title="Visit page outside Confluence" rel="nofollow">RuntimeContextImpl<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span>.execute().</p>

<p>There's one more thing that should be mentioned here. Continuations (and hence JacobRunnables) don't "stay" in the VPU queues. They just get popped, executed and that's it. So if an abstraction must last more than one execution, it should simply fork itself. This explains why in our Sequence example already pasted above we see the line:</p>

<div class="code"><div class="codeContent">
<pre class="code-java">instance(<span class="code-keyword">new</span> SequenceChildRunner(currentChild+1));</pre>
</div></div>

<p>This simple adds a new !ChildRunner that will monitor the next child completion. If you browse PXE's activities code you will even find things like instance(this) which directly enqueues a new instance of the same Jacob abstraction.</p>

<h3><a name="Jacob-Walkingthroughexamples"></a>Walking through examples</h3>

<h4><a name="Jacob-While"></a>While</h4>

<div class="code"><div class="codeContent">
<pre class="code-xml">&lt;process name=<span class="code-quote">"while1"</span> 
    targetNamespace=<span class="code-quote">"http://pxe/bpel/unit-test"</span> 
    <span class="code-keyword">xmlns:bpws</span>=<span class="code-quote">"http://schemas.xmlsoap.org/ws/2003/03/business-process/"</span>
    xmlns=<span class="code-quote">"http://schemas.xmlsoap.org/ws/2003/03/business-process/"</span>
    <span class="code-keyword">xmlns:tns</span>=<span class="code-quote">"http://pxe/bpel/unit-test"</span>
    <span class="code-keyword">xmlns:xsd</span>=<span class="code-quote">"http://www.w3.org/2001/XMLSchema"</span>
    <span class="code-keyword">xmlns:test</span>=<span class="code-quote">"http://pxe/bpel/unit-test.wsdl"</span> 
    suppressJoinFailure=<span class="code-quote">"yes"</span>&gt;
   <span class="code-tag">&lt;partnerLinks&gt;</span>
      &lt;partnerLink name=<span class="code-quote">"testPartnerLink"</span> 
         partnerLinkType=<span class="code-quote">"test:TestPartnerLinkType"</span> 
         myRole=<span class="code-quote">"me"</span> /&gt;
   <span class="code-tag">&lt;/partnerLinks&gt;</span>

   <span class="code-tag">&lt;variables&gt;</span>
     <span class="code-tag">&lt;variable name=<span class="code-quote">"var1"</span> messageType=<span class="code-quote">"test:TestMessage2"</span>/&gt;</span>
   <span class="code-tag">&lt;/variables&gt;</span>

   <span class="code-tag">&lt;sequence&gt;</span>
       &lt;receive 
          createInstance=<span class="code-quote">"yes"</span>
          name=<span class="code-quote">"startReceive"</span>
          partnerLink=<span class="code-quote">"testPartnerLink"</span>
          portType=<span class="code-quote">"test:TestPortType"</span>
          operation=<span class="code-quote">"testOperation"</span>
          variable=<span class="code-quote">"var1"</span>/&gt;
      <span class="code-tag">&lt;while condition=<span class="code-quote">"bpws:getVariableData('var1', 'TestPart') &amp;lt; 10"</span>&gt;</span>
        <span class="code-tag">&lt;assign&gt;</span>
           <span class="code-tag">&lt;copy&gt;</span>
             <span class="code-tag">&lt;from expression=<span class="code-quote">"bpws:getVariableData('var1', 'TestPart') + 1"</span>/&gt;</span>
             <span class="code-tag">&lt;to variable=<span class="code-quote">"var1"</span> part=<span class="code-quote">"TestPart"</span>/&gt;</span>
          <span class="code-tag">&lt;/copy&gt;</span>
        <span class="code-tag">&lt;/assign&gt;</span>
       <span class="code-tag">&lt;/while&gt;</span>
       &lt;reply name=<span class="code-quote">"endReply"</span> 
              operation=<span class="code-quote">"testOperation"</span> 
              partnerLink=<span class="code-quote">"testPartnerLink"</span>
              portType=<span class="code-quote">"test:TestPortType"</span> 
              variable=<span class="code-quote">"var1"</span>/&gt;
   <span class="code-tag">&lt;/sequence&gt;</span>
<span class="code-tag">&lt;/process&gt;</span></pre>
</div></div>

<p>Everything starts with a receive. So our entry point here in our Jacob-focused discussion is going to be <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/bpel-runtime/src/main/java/org/apache/ode/bpel/runtime/BpelProcess.java" title="Visit page outside Confluence" rel="nofollow">BpelProcess<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span>.PartnerLinkMyRoleImpl.inputMsgRcvd(). The code that matters to us now is the following (executed when a message is targeted at a createInstance receive):</p>

<div class="code"><div class="codeContent">
<pre class="code-java">BpelRuntimeContextImpl instance = createRuntimeContext(newInstance, <span class="code-keyword">new</span> PROCESS(_oprocess), messageExchange);
...
    <span class="code-comment">// run the vpu
</span>    instance.execute();</pre>
</div></div>

<p>If you check the code executed by !BpelRuntimeContextImpl constructor you'll see among other things the following:</p>

<div class="code"><div class="codeContent">
<pre class="code-java"><span class="code-keyword">if</span> (PROCESS != <span class="code-keyword">null</span>) {
   vpu.inject(PROCESS);
}</pre>
</div></div>

<p>The process itself get injected. When executed, <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/bpel-runtime/src/main/java/org/apache/ode/bpel/runtime/PROCESS.java" title="Visit page outside Confluence" rel="nofollow">PROCESS<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span> just instantiates a scope to control the execution of its child activity and starts listening on compensation and completion channel. From the process we go to a scope, then our main sequence and finally our receive.</p>

<p>Receives are just mapped to a pick onMessage so its Jacob implementation should be looked for in <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/bpel-runtime/src/main/java/org/apache/ode/bpel/runtime/PICK.java" title="Visit page outside Confluence" rel="nofollow">PICK<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span>. The <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/bpel-runtime/src/main/java/org/apache/ode/bpel/runtime/PICK.java" title="Visit page outside Confluence" rel="nofollow">PICK<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span> is just about isolating the right correlations and selecting a message for it, then waiting for the message. In our createInstance case we'll be more interested in the following code, located in <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/bpel-runtime/src/main/java/org/apache/ode/bpel/runtime/BpelRuntimeContextImpl.java" title="Visit page outside Confluence" rel="nofollow">BpelRuntimeContextImpl<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span>.select() (and called by PICK):</p>

<div class="code"><div class="codeContent">
<pre class="code-java"><span class="code-keyword">if</span> (_instantiatingMessageExchange != <span class="code-keyword">null</span> &amp;&amp; _dao.getState() == ProcessState.STATE_READY) {
   <span class="code-keyword">for</span> (<span class="code-object">int</span> i = 0 ; i &lt; correlators.size(); ++i) {
     CorrelatorDAO ci = correlators.get(i);
     <span class="code-keyword">if</span> (ci.equals(_dao.getInstantiatingCorrelator())) {
       inputMsgMatch(pickResponseChannelStr, i, _instantiatingMessageExchange);
       <span class="code-keyword">return</span>;
     }
   }
}</pre>
</div></div>

<p>Which just happens to call something like:</p>

<div class="code"><div class="codeContent">
<pre class="code-java">vpu.inject(<span class="code-keyword">new</span> JacobRunnable() {
   <span class="code-keyword">public</span> void run() {
      PickResponseChannel responseChannel = importChannel(responsechannel, PickResponseChannel.class);
      responseChannel.onRequestRcvd(idx, mex);
   }
});</pre>
</div></div>

<p>That's where things really start. When injected, this abstraction just calls the response channel for our receive. The other side of this channel is implemented as a ML in the <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/bpel-runtime/src/main/java/org/apache/ode/bpel/runtime/PICK.java" title="Visit page outside Confluence" rel="nofollow">PICK<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span>:</p>

<div class="code"><div class="codeContent">
<pre class="code-java">object(<span class="code-keyword">false</span>, <span class="code-keyword">new</span> PickResponseML(_pickResponseChannel) {
    <span class="code-keyword">public</span> void onRequestRcvd(<span class="code-object">int</span> selectorIdx, <span class="code-object">Object</span> msgex) {
        ...
      ActivityInfo child = <span class="code-keyword">new</span> ActivityInfo(genMonotonic(), onMessage.activity, _self.self, _self.parent);
      instance(createChild(child,_scopeFrame,_linkFrame));
    }
});</pre>
</div></div>

<p>This method just does what a receive needs to do (like variable and correlation initialization) and creates a new child. When dealing with a real pick, this child would be the onMessage activity, however in the case of a receive, this is an empty activity. So when does our receive completes? Well, when the child completes. As you can see on the child constructor, we're passing the same ParentScopeML that we've been provided. So when the child completes, the receive's parent is notified which means to our receive doesn't need to do it itself. And an empty immediately completes: </p>

<div class="code"><div class="codeContent">
<pre class="code-java">_self.parent.completed(<span class="code-keyword">null</span>, CompensationHandler.emptySet());</pre>
</div></div>

<p>The parent sequence gets notified almost immediately after the onRequestRcvd() methods finishes.</p>

<p>Now how does our sequence gets the control back? Well, once again, let's look at the ML, the other side of the channel. As one of the most important job of the VPU is matching channels invocations and MLs, we'll get to the <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/bpel-runtime/src/main/java/org/apache/ode/bpel/runtime/SEQUENCE.java" title="Visit page outside Confluence" rel="nofollow">sequence<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span> by its ParentScopeML implementation:</p>

<div class="code"><div class="codeContent">
<pre class="code-java">class SEQUENCE <span class="code-keyword">extends</span> ACTIVITY {
  ...
  <span class="code-keyword">private</span> class ACTIVE <span class="code-keyword">extends</span> BpelJacobRunnable {
    ....
    <span class="code-keyword">public</span> void run() {
      ...
      object(<span class="code-keyword">new</span> ParentScopeML(_child.parent) {
        <span class="code-keyword">public</span> void compensate(OScope scope, SynchChannel ret) {
          _self.parent.compensate(scope,ret);
          instance(ACTIVE.<span class="code-keyword">this</span>);
        }

        <span class="code-keyword">public</span> void completed(FaultData faultData, Set&lt;CompensationHandler&gt; compensations) {
          HashSet&lt;CompensationHandler&gt; comps = <span class="code-keyword">new</span> HashSet&lt;CompensationHandler&gt;(_compensations);
          comps.addAll(compensations);
          <span class="code-keyword">if</span> (faultData != <span class="code-keyword">null</span> || _terminateRequested || _remaining.size() &lt;= 1) {
            _self.parent.completed(faultData, comps);
          } <span class="code-keyword">else</span> /* !fault &amp;&amp; ! terminateRequested &amp;&amp; !remaining.isEmpty */ {
            ArrayList&lt;OActivity&gt; remaining = <span class="code-keyword">new</span> ArrayList&lt;OActivity&gt;(_remaining);
            remaining.remove(0);
            instance(<span class="code-keyword">new</span> SEQUENCE(_self, _scopeFrame, _linkFrame, remaining, comps));
          }
        }
      }));
    }
  }
  ...
}</pre>
</div></div>

<p>The method that will get executed is of course the completed() method. It simply completes if a fault has been thrown, a termination has been requested and if no child activities remain. Being of an optimistic nature, we'll check what happens when everything goes just fine. In this second case a remaining activity is removed and the <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/bpel-runtime/src/main/java/org/apache/ode/bpel/runtime/SEQUENCE.java" title="Visit page outside Confluence" rel="nofollow">SEQUENCE<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span> abstraction itself is reinstantiated. Which leads us to what the <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/bpel-runtime/src/main/java/org/apache/ode/bpel/runtime/SEQUENCE.java" title="Visit page outside Confluence" rel="nofollow">SEQUENCE<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span> does:</p>

<div class="code"><div class="codeContent">
<pre class="code-java"><span class="code-keyword">public</span> void run() {
   <span class="code-keyword">final</span> ActivityInfo child = <span class="code-keyword">new</span>  ActivityInfo(genMonotonic(),
          _remaining.get(0),
          newChannel(TerminationChannel.class), newChannel(ParentScopeChannel.class));
   instance(createChild(child, _scopeFrame, _linkFrame));
   instance(<span class="code-keyword">new</span> ACTIVE(child));
}</pre>
</div></div>

<p>As you can see, it just instantiates the next child abstraction and also another abstraction named ACTIVE. So what's this ACTIVE that we've already seen a bit before? Well, it's just the abstraction that keeps on following child activities when they execute. It's more like a convention on all containment-based activity in PXE (while, sequence, pick, ...) that the main activity abstraction just kicks off the processing. Then an ACTIVE (also sometimes called WAITER) abstraction takes care of following the children.</p>

<p>Continuing to the next step, we've just instantiated an abstraction for the <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/bpel-runtime/src/main/java/org/apache/ode/bpel/runtime/WHILE.java" title="Visit page outside Confluence" rel="nofollow">while<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span> in our example process, as it's the next child of the sequence. So what happens there?</p>

<div class="code"><div class="codeContent">
<pre class="code-java"><span class="code-keyword">public</span> void run() {
    <span class="code-object">boolean</span> condResult = <span class="code-keyword">false</span>;
    <span class="code-keyword">try</span> {
      condResult = checkCondition();
    } <span class="code-keyword">catch</span> (FaultException fe) {
      _self.parent.completed(createFault(fe.getQName(), _self.o),_compHandlers);
      <span class="code-keyword">return</span>;
    }
    <span class="code-keyword">if</span> (condResult) {
      ActivityInfo child = <span class="code-keyword">new</span> ActivityInfo(genMonotonic(),
              getOWhile().activity,
              newChannel(TerminationChannel.class), newChannel(ParentScopeChannel.class));
      instance(createChild(child, _scopeFrame, _linkFrame));
      instance(<span class="code-keyword">new</span> WAITER(child));
    } <span class="code-keyword">else</span> /* stop. */ {
      _self.parent.completed(<span class="code-keyword">null</span>, _compHandlers);
    }
  }</pre>
</div></div>

<p>Now you should be getting more familiar with that sort of code. First step is evaluating the <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/bpel-runtime/src/main/java/org/apache/ode/bpel/runtime/WHILE.java" title="Visit page outside Confluence" rel="nofollow">while<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span> condition. If it turns out it's true, then a child abstraction gets created as well as a WAITER to follow its execution. The WAITER implementation is again pretty straightforward:</p>

<div class="code"><div class="codeContent">
<pre class="code-java"><span class="code-keyword">private</span> class WAITER <span class="code-keyword">extends</span> BpelJacobRunnable {
    <span class="code-keyword">private</span> ActivityInfo _child;
    <span class="code-keyword">private</span> <span class="code-object">boolean</span> _terminated;

    WAITER(ActivityInfo child) {
      _child = child;
    }

    <span class="code-keyword">public</span> void run() {
      object(<span class="code-keyword">false</span>, <span class="code-keyword">new</span> TerminationML(_self.self) {
        <span class="code-keyword">public</span> void terminate() {
          _terminated = <span class="code-keyword">true</span>;
          _child.self.terminate();
          instance(WAITER.<span class="code-keyword">this</span>);
        }
      }.or(<span class="code-keyword">new</span> ParentScopeML(_child.parent) {
        <span class="code-keyword">public</span> void compensate(OScope scope, SynchChannel ret) {
          _self.parent.compensate(scope,ret);
          instance(WAITER.<span class="code-keyword">this</span>);
        }
        <span class="code-keyword">public</span> void completed(FaultData faultData, Set&lt;CompensationHandler&gt; compensations) {
          _compHandlers.addAll(compensations);
          <span class="code-keyword">if</span> (_terminated || faultData != <span class="code-keyword">null</span>)
            _self.parent.completed(faultData, compensations);
          <span class="code-keyword">else</span>
            instance(WHILE.<span class="code-keyword">this</span>);
        }
      }));
    }
  }</pre>
</div></div>

<p>Termination and compensation aren't doing anything really interesting. Completion, just like for the sequence, re-instantiates the <span class="nobr"><a href="http://svn.apache.org/repos/asf/ode/trunk/bpel-runtime/src/main/java/org/apache/ode/bpel/runtime/WHILE.java" title="Visit page outside Confluence" rel="nofollow">WHILE<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span> abstraction. And that's how we get our loop, by re-instantiating the main WHILE abstraction (again evaluating the condition and creating a child if it's true).</p>

<p>Finally, when the while condition becomes false, it notifies its parent channel. The sequence then goes to our last activity: reply. As expected, the reply replies, just sending the variable content and notifying its parent for completion. The sequence has no more children to execute so it also notifies its own parent, which is the process. We then just declare the process to be completed and that's it! We're done!</p></div>


</td></tr></table></div>
<p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tr>
        <td height="12" background="http://cwiki.apache.org/confluence/images/border/border_bottom.gif"><img src="http://cwiki.apache.org/confluence/images/border/spacer.gif" width="1" height="1" border="0"/></td>
    </tr>
</table>

<div class="smalltext">
    Powered by
    <a href="http://www.atlassian.com/software/confluence/default.jsp?clicked=footer" class="smalltext">Atlassian Confluence</a>
    (Version: 2.2.9 Build:#527 Sep 07, 2006)
    -
    <a href="http://jira.atlassian.com/secure/BrowseProject.jspa?id=10470" class="smalltext">Bug/feature request</a><br/>
    <br>
    <a href="http://cwiki.apache.org/confluence/users/viewnotifications.action">Unsubscribe or edit your notifications preferences</a>

</div>

</body>
</html>


Mime
View raw message