mina-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] Apache MINA: Tutorial on ProtocolCodecFilter (for Mina 2.x) (page edited)
Date Tue, 30 Dec 2008 09:11: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/MINA">MINA</a> :
            <a href="http://cwiki.apache.org/confluence/display/MINA/Tutorial+on+ProtocolCodecFilter+%28for+Mina+2.x%29">Tutorial on ProtocolCodecFilter (for Mina 2.x)</a>
        </span>
    </div>

     <p>
        <a href="http://cwiki.apache.org/confluence/display/MINA/Tutorial+on+ProtocolCodecFilter+%28for+Mina+2.x%29">Tutorial on ProtocolCodecFilter (for Mina 2.x)</a>
        has been edited by             <a href="http://cwiki.apache.org/confluence/display/~mbosteels.dns@gmail.com">Maarten Bosteels</a>
            <span class="smallfont">(Dec 30, 2008)</span>.
     </p>
    
     <p>
                 <a href="http://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=71339&originalVersion=8&revisedVersion=9">(View changes)</a>
     </p>

    <span class="label">Content:</span><br/>
    <div class="greybox wiki-content"><p>This tutorial tries to explain why and how to use a <tt>ProtocolCodecFilter</tt>.</p>

<h2><a name="TutorialonProtocolCodecFilter%28forMina2.x%29-Whyusea%7B%7BProtocolCodecFilter%7D%7D%3F"></a>Why use a <tt>ProtocolCodecFilter</tt>?</h2>

<ul>
	<li>TCP guarantess delivery of all packets in the correct order.<br/>
But there is no guarantee that one write operation on the sender-side will result in one read event on the receiving side.<br/>
see <span class="nobr"><a href="http://en.wikipedia.org/wiki/IPv4#Fragmentation_and_reassembly" title="Visit page outside Confluence" rel="nofollow">http://en.wikipedia.org/wiki/IPv4#Fragmentation_and_reassembly<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span> and <span class="nobr"><a href="http://en.wikipedia.org/wiki/Nagle%27s_algorithm" title="Visit page outside Confluence" rel="nofollow">http://en.wikipedia.org/wiki/Nagle%27s_algorithm<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span><br/>
In MINA terminology: without a ProtocolCodecFilter one call of <tt>IoSession.write(Object message)</tt> by the sender can result in multiple <tt>messageReceived(IoSession session, Object message)</tt> events on the receiver; and multiple calls of <tt>IoSession.write(Object message)</tt> can lead to a single messageReceived event. You might not encounter this behavior when client and server are running on the same host (or an a local network) but your applications should be able to cope with this.</li>
	<li>Most network applications need a way to find out where the current message ends and where the next message starts.</li>
	<li>You could implement all this logic in your IoHandler, but adding a <tt>ProtocolCodecFilter</tt> will make your code much cleaner and easier to maintain.<br/>
It allows you to separate your protocol logic from your business logic (<tt>IoHandler</tt>).</li>
</ul>


<h2><a name="TutorialonProtocolCodecFilter%28forMina2.x%29-How%3F"></a>How ?</h2>

<p>Your application is basically just receiving a bunch of bytes and you need to convert these bytes into messages (higher level objects).</p>

<p>There are three common techniques for splitting the stream of bytes into messages:</p>
<ul>
	<li>use fixed length messages</li>
	<li>use a fixed length header that indicates the length of the body</li>
	<li>using a delimiter; for example many text-based protocols append a newline (or CR LF pair) after every message (<span class="nobr"><a href="http://www.faqs.org/rfcs/rfc977.html" title="Visit page outside Confluence" rel="nofollow">http://www.faqs.org/rfcs/rfc977.html<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span>)</li>
</ul>


<p>In this tutorial we will use the first and second method since they are definitely easier to implement. Afterwards we will look at using a delimiter.</p>

<h2><a name="TutorialonProtocolCodecFilter%28forMina2.x%29-Example"></a>Example</h2>

<p>We will develop a (pretty useless) graphical chargen server to illustrate how to implement your own protocol codec (<tt>ProtocolEncoder</tt>, <tt>ProtocolDecoder</tt>, and <tt>ProtocolCodecFactory</tt>).<br/>
The protocol is really simple. This is the layout of a request message:</p>

<table class='confluenceTable'><tbody>
<tr>
<th class='confluenceTh'>4 bytes</th>
<th class='confluenceTh'>4 bytes</th>
<th class='confluenceTh'>4 bytes</th>
</tr>
<tr>
<td class='confluenceTd'>width</td>
<td class='confluenceTd'>height</td>
<td class='confluenceTd'>numchars</td>
</tr>
</tbody></table>

<ul>
	<li>width: the width of the requested image   (an integer in network byte-order)</li>
	<li>height: the height of the requested image (an integer in network byte-order)</li>
	<li>numchars: the number of chars to generate (an integer in network byte-order)</li>
</ul>


<p>The server responds with two images of the requested dimensions, with the requested number of characters painted on it.<br/>
This is the layout of a response message:</p>

<table class='confluenceTable'><tbody>
<tr>
<th class='confluenceTh'>4 bytes</th>
<th class='confluenceTh'>variable length body</th>
<th class='confluenceTh'>4 bytes</th>
<th class='confluenceTh'>variable length body</th>
</tr>
<tr>
<td class='confluenceTd'>length1</td>
<td class='confluenceTd'>image1</td>
<td class='confluenceTd'>length2</td>
<td class='confluenceTd'>image2</td>
</tr>
</tbody></table>

<ul>
	<li>length1: the number of bytes used by image1</li>
	<li>image1: an image in PNG format</li>
	<li>length2: the number of bytes used by image2</li>
	<li>image2: an image in PNG format</li>
</ul>


<p>Overview of the classes we need for encoding and decoding requests and responses:</p>
<ul>
	<li><tt>ImageRequest</tt>: a simple POJO representing a request to our ImageServer.</li>
	<li><tt>ImageRequestEncoder</tt>: encodes ImageRequest objects into protocol-specific data (used by the client)</li>
	<li><tt>ImageRequestDecoder</tt>: decodes protocol-specific data into ImageRequest objects (used by the server)</li>
	<li><tt>ImageResponse</tt>: a simple POJO representing a response from our ImageServer.</li>
	<li><tt>ImageResponseEncoder</tt>: used by the server for encoding ImageResponse objects</li>
	<li><tt>ImageResponseDecoder</tt>: used by the client for decoding ImageResponse objects</li>
	<li><tt>ImageCodecFactory</tt>: this class creates the necesarry encoders and decoders</li>
</ul>


<div class="code"><div class="codeContent">
<pre class="code-java"><span class="code-keyword">public</span> class ImageRequest {

    <span class="code-keyword">private</span> <span class="code-object">int</span> width;
    <span class="code-keyword">private</span> <span class="code-object">int</span> height;
    <span class="code-keyword">private</span> <span class="code-object">int</span> numberOfCharacters;

    <span class="code-keyword">public</span> ImageRequest(<span class="code-object">int</span> width, <span class="code-object">int</span> height, <span class="code-object">int</span> numberOfCharacters) {
        <span class="code-keyword">this</span>.width = width;
        <span class="code-keyword">this</span>.height = height;
        <span class="code-keyword">this</span>.numberOfCharacters = numberOfCharacters;
    }

    <span class="code-keyword">public</span> <span class="code-object">int</span> getWidth() {
        <span class="code-keyword">return</span> width;
    }

    <span class="code-keyword">public</span> <span class="code-object">int</span> getHeight() {
        <span class="code-keyword">return</span> height;
    }

    <span class="code-keyword">public</span> <span class="code-object">int</span> getNumberOfCharacters() {
        <span class="code-keyword">return</span> numberOfCharacters;
    }
}</pre>
</div></div>
<p>Encoding is usually simpler than decoding, so let's start with the <tt>ImageRequestEncoder</tt>:</p>
<div class="code"><div class="codeContent">
<pre class="code-java"><span class="code-keyword">public</span> class ImageRequestEncoder <span class="code-keyword">implements</span> ProtocolEncoder {

    <span class="code-keyword">public</span> void encode(IoSession session, <span class="code-object">Object</span> message, ProtocolEncoderOutput out) <span class="code-keyword">throws</span> Exception {
        ImageRequest request = (ImageRequest) message;
        IoBuffer buffer = IoBuffer.allocate(12, <span class="code-keyword">false</span>);
        buffer.putInt(request.getWidth());
        buffer.putInt(request.getHeight());
        buffer.putInt(request.getNumberOfCharacters());
        buffer.flip();
        out.write(buffer);
    }

    <span class="code-keyword">public</span> void dispose(IoSession session) <span class="code-keyword">throws</span> Exception {
        <span class="code-comment">// nothing to dispose
</span>    }
}</pre>
</div></div>
<p>Remarks:</p>
<ul>
	<li>MINA will call the encode function for all messages in the IoSession's write queue. Since our client will only write <tt>ImageRequest</tt> objects, we can safely cast message to <tt>ImageRequest</tt>.</li>
	<li>We allocate a new <tt>IoBuffer</tt> from the heap. It's best to avoid using direct buffers, since generally heap buffers perform better.<br/>
&nbsp;see <span class="nobr"><a href="http://issues.apache.org/jira/browse/DIRMINA-289" title="Visit page outside Confluence" rel="nofollow">http://issues.apache.org/jira/browse/DIRMINA-289<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span>)</li>
	<li>You do not have to release the buffer, MINA will do it for you, see <span class="nobr"><a href="http://mina.apache.org/report/trunk/apidocs/org/apache/mina/common/IoBuffer.html" title="Visit page outside Confluence" rel="nofollow">http://mina.apache.org/report/trunk/apidocs/org/apache/mina/common/IoBuffer.html<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span></li>
	<li>In the <tt>dispose()</tt> method you should release all resources acquired during encoding for the specified session. If there is nothing to dispose you could let your encoder inherit from <tt>ProtocolEncoderAdapter</tt>.</li>
</ul>


<p>Now let's have a look at the decoder. The <tt>CumulativeProtocolDecoder</tt> is a great help for writing your own decoder: it will buffer all incoming data until your decoder decides it can do something with it.<br/>
In this case the message has a fixed size, so it's easiest to wait until all data is available:</p>
<div class="code"><div class="codeContent">
<pre class="code-java"><span class="code-keyword">public</span> class ImageRequestDecoder <span class="code-keyword">extends</span> CumulativeProtocolDecoder {

    <span class="code-keyword">protected</span> <span class="code-object">boolean</span> doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) <span class="code-keyword">throws</span> Exception {
        <span class="code-keyword">if</span> (in.remaining() &gt;= 12) {
            <span class="code-object">int</span> width = in.getInt();
            <span class="code-object">int</span> height = in.getInt();
            <span class="code-object">int</span> numberOfCharachters = in.getInt();
            ImageRequest request = <span class="code-keyword">new</span> ImageRequest(width, height, numberOfCharachters);
            out.write(request);
            <span class="code-keyword">return</span> <span class="code-keyword">true</span>;
        } <span class="code-keyword">else</span> {
            <span class="code-keyword">return</span> <span class="code-keyword">false</span>;
        }
    }
}</pre>
</div></div>
<p>Remarks:</p>
<ul>
	<li>everytime a complete message is decoded, you should write it to the <tt>ProtocolDecoderOutput</tt>; these messages will travel along the filter-chain and eventually arrive in your <tt>IoHandler.messageReceived</tt> method</li>
	<li>you are not responsible for releasing the <tt>IoBuffer</tt></li>
	<li>when there is not enough data available to decode a message, just return false</li>
</ul>


<p>The response is also a very simple POJO:</p>
<div class="code"><div class="codeContent">
<pre class="code-java"><span class="code-keyword">public</span> class ImageResponse {

    <span class="code-keyword">private</span> BufferedImage image1;

    <span class="code-keyword">private</span> BufferedImage image2;

    <span class="code-keyword">public</span> ImageResponse(BufferedImage image1, BufferedImage image2) {
        <span class="code-keyword">this</span>.image1 = image1;
        <span class="code-keyword">this</span>.image2 = image2;
    }

    <span class="code-keyword">public</span> BufferedImage getImage1() {
        <span class="code-keyword">return</span> image1;
    }

    <span class="code-keyword">public</span> BufferedImage getImage2() {
        <span class="code-keyword">return</span> image2;
    }
}</pre>
</div></div>
<p>Encoding the response is also trivial:</p>
<div class="code"><div class="codeContent">
<pre class="code-java"><span class="code-keyword">public</span> class ImageResponseEncoder <span class="code-keyword">extends</span> ProtocolEncoderAdapter {

    <span class="code-keyword">public</span> void encode(IoSession session, <span class="code-object">Object</span> message, ProtocolEncoderOutput out) <span class="code-keyword">throws</span> Exception {
        ImageResponse imageResponse = (ImageResponse) message;
        <span class="code-object">byte</span>[] bytes1 = getBytes(imageResponse.getImage1());
        <span class="code-object">byte</span>[] bytes2 = getBytes(imageResponse.getImage2());
        <span class="code-object">int</span> capacity = bytes1.length + bytes2.length + 8;
        IoBuffer buffer = IoBuffer.allocate(capacity, <span class="code-keyword">false</span>);
        buffer.setAutoExpand(<span class="code-keyword">true</span>);
        buffer.putInt(bytes1.length);
        buffer.put(bytes1);
        buffer.putInt(bytes2.length);
        buffer.put(bytes2);
        buffer.flip();
        out.write(buffer);
    }

    <span class="code-keyword">private</span> <span class="code-object">byte</span>[] getBytes(BufferedImage image) <span class="code-keyword">throws</span> IOException {
        ByteArrayOutputStream baos = <span class="code-keyword">new</span> ByteArrayOutputStream();
        ImageIO.write(image, <span class="code-quote">"PNG"</span>, baos);
        <span class="code-keyword">return</span> baos.toByteArray();
    }
}</pre>
</div></div>
<p>Remarks:</p>
<ul>
	<li>when it is impossible to calculate the length of the IoBuffer beforehand, you can use an auto-expanding buffer by calling <tt>buffer.setAutoExpand(true);</tt></li>
</ul>


<p>Now let's have a look at decoding the response:</p>
<div class="code"><div class="codeContent">
<pre class="code-java"><span class="code-keyword">public</span> class ImageResponseDecoder <span class="code-keyword">extends</span> CumulativeProtocolDecoder {

    <span class="code-keyword">private</span> <span class="code-keyword">static</span> <span class="code-keyword">final</span> <span class="code-object">String</span> DECODER_STATE_KEY = ImageResponseDecoder.class.getName() + <span class="code-quote">".STATE"</span>;

    <span class="code-keyword">public</span> <span class="code-keyword">static</span> <span class="code-keyword">final</span> <span class="code-object">int</span> MAX_IMAGE_SIZE = 5 * 1024 * 1024;

    <span class="code-keyword">private</span> <span class="code-keyword">static</span> class DecoderState {
        BufferedImage image1;
    }

    <span class="code-keyword">protected</span> <span class="code-object">boolean</span> doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) <span class="code-keyword">throws</span> Exception {
        DecoderState decoderState = (DecoderState) session.getAttribute(DECODER_STATE_KEY);
        <span class="code-keyword">if</span> (decoderState == <span class="code-keyword">null</span>) {
            decoderState = <span class="code-keyword">new</span> DecoderState();
            session.setAttribute(DECODER_STATE_KEY, decoderState);
        }
        <span class="code-keyword">if</span> (decoderState.image1 == <span class="code-keyword">null</span>) {
            <span class="code-comment">// <span class="code-keyword">try</span> to read first image
</span>            <span class="code-keyword">if</span> (in.prefixedDataAvailable(4, MAX_IMAGE_SIZE)) {
                decoderState.image1 = readImage(in);
            } <span class="code-keyword">else</span> {
                <span class="code-comment">// not enough data available to read first image
</span>                <span class="code-keyword">return</span> <span class="code-keyword">false</span>;
            }
        }
        <span class="code-keyword">if</span> (decoderState.image1 != <span class="code-keyword">null</span>) {
            <span class="code-comment">// <span class="code-keyword">try</span> to read second image
</span>            <span class="code-keyword">if</span> (in.prefixedDataAvailable(4, MAX_IMAGE_SIZE)) {
                BufferedImage image2 = readImage(in);
                ImageResponse imageResponse = <span class="code-keyword">new</span> ImageResponse(decoderState.image1, image2);
                out.write(imageResponse);
                decoderState.image1 = <span class="code-keyword">null</span>;
                <span class="code-keyword">return</span> <span class="code-keyword">true</span>;
            } <span class="code-keyword">else</span> {
                <span class="code-comment">// not enough data available to read second image
</span>                <span class="code-keyword">return</span> <span class="code-keyword">false</span>;
            }
        }
        <span class="code-keyword">return</span> <span class="code-keyword">false</span>;
    }

    <span class="code-keyword">private</span> BufferedImage readImage(IoBuffer in) <span class="code-keyword">throws</span> IOException {
        <span class="code-object">int</span> length = in.getInt();
        <span class="code-object">byte</span>[] bytes = <span class="code-keyword">new</span> <span class="code-object">byte</span>[length];
        in.get(bytes);
        ByteArrayInputStream bais = <span class="code-keyword">new</span> ByteArrayInputStream(bytes);
        <span class="code-keyword">return</span> ImageIO.read(bais);
    }
}</pre>
</div></div>
<p>Remarks:</p>
<ul>
	<li>We store the state of the decoding process in a session attribute. It would also be possible to store this state in the Decoder object itself but this has several disadvantages:
	<ul>
		<li>every IoSession would need its own Decoder instance</li>
		<li>MINA ensures that there will never be more than one thread simultaneously executing the decode() function for the same IoSession, but it does not guarantee that it will always be the same thread. Suppose the first piece of data is handled by thread-1 who decides it cannot yet decode, when the next piece of data arrives, it could be handled by another thread. To avoid visibility problems, you must properly synchronize access to this decoder state (IoSession attributes are stored in a <tt>ConcurrentHashMap</tt>, so they are automatically visible to other threads).</li>
		<li>a discussion on the mailing list has lead to this conclusion: choosing between storing state in the IoSession or in the Decoder instance itself is more a matter of taste. To ensure that no two threads will run the decode method for the same IoSession, MINA needs to do some form of synchronization =&gt; this synchronization will also ensure you can't have the visibility problem described above.<br/>
(Thanks to Adam Fisk for pointing this out)<br/>
see <span class="nobr"><a href="http://www.nabble.com/Tutorial-on-ProtocolCodecFilter,-state-and-threads-t3965413.html" title="Visit page outside Confluence" rel="nofollow">http://www.nabble.com/Tutorial-on-ProtocolCodecFilter,-state-and-threads-t3965413.html<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span></li>
	</ul>
	</li>
	<li><tt>IoBuffer.prefixedDataAvailable()</tt> is very convenient when your protocol uses a length-prefix; it supports a prefix of 1, 2 or 4 bytes.</li>
	<li>don't forget to reset the decoder state when you've decoded a response (removing the session attribute is another way to do it)</li>
</ul>


<p>If the response would consist of a single image, we would not need to store decoder state:</p>
<div class="code"><div class="codeContent">
<pre class="code-java"><span class="code-keyword">protected</span> <span class="code-object">boolean</span> doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) <span class="code-keyword">throws</span> Exception {
        <span class="code-keyword">if</span> (in.prefixedDataAvailable(4)) {
            <span class="code-object">int</span> length = in.getInt();
            <span class="code-object">byte</span>[] bytes = <span class="code-keyword">new</span> <span class="code-object">byte</span>[length];
            in.get(bytes);
            ByteArrayInputStream bais = <span class="code-keyword">new</span> ByteArrayInputStream(bytes);
            BufferedImage image = ImageIO.read(bais);
            out.write(image);
            <span class="code-keyword">return</span> <span class="code-keyword">true</span>;
        } <span class="code-keyword">else</span> {
            <span class="code-keyword">return</span> <span class="code-keyword">false</span>;
        }
    }</pre>
</div></div>
<p>Now let's glue it all together:</p>
<div class="code"><div class="codeContent">
<pre class="code-java"><span class="code-keyword">public</span> class ImageCodecFactory <span class="code-keyword">implements</span> ProtocolCodecFactory {
    <span class="code-keyword">private</span> ProtocolEncoder encoder;
    <span class="code-keyword">private</span> ProtocolDecoder decoder;

    <span class="code-keyword">public</span> ImageCodecFactory(<span class="code-object">boolean</span> client) {
        <span class="code-keyword">if</span> (client) {
            encoder = <span class="code-keyword">new</span> ImageRequestEncoder();
            decoder = <span class="code-keyword">new</span> ImageResponseDecoder();
        } <span class="code-keyword">else</span> {
            encoder = <span class="code-keyword">new</span> ImageResponseEncoder();
            decoder = <span class="code-keyword">new</span> ImageRequestDecoder();
        }
    }

    <span class="code-keyword">public</span> ProtocolEncoder getEncoder(IoSession ioSession) <span class="code-keyword">throws</span> Exception {
        <span class="code-keyword">return</span> encoder;
    }

    <span class="code-keyword">public</span> ProtocolDecoder getDecoder(IoSession ioSession) <span class="code-keyword">throws</span> Exception {
        <span class="code-keyword">return</span> decoder;
    }
}</pre>
</div></div>
<p>Remarks:</p>
<ul>
	<li>for every new session, MINA will ask the <tt>ImageCodecFactory</tt> for an encoder and a decoder.</li>
	<li>since our encoders and decoders store no conversational state, it is safe to let all sessions share a single instance.</li>
</ul>


<p>This is how the server would use the <tt>ProtocolCodecFactory</tt>:</p>
<div class="code"><div class="codeContent">
<pre class="code-java"><span class="code-keyword">public</span> class ImageServer {
    <span class="code-keyword">public</span> <span class="code-keyword">static</span> <span class="code-keyword">final</span> <span class="code-object">int</span> PORT = 33789;

    <span class="code-keyword">public</span> <span class="code-keyword">static</span> void main(<span class="code-object">String</span>[] args) <span class="code-keyword">throws</span> IOException {
        ImageServerIoHandler handler = <span class="code-keyword">new</span> ImageServerIoHandler();
        NioSocketAcceptor acceptor = <span class="code-keyword">new</span> NioSocketAcceptor();
        acceptor.getFilterChain().addLast(<span class="code-quote">"protocol"</span>, <span class="code-keyword">new</span> ProtocolCodecFilter(<span class="code-keyword">new</span> ImageCodecFactory(<span class="code-keyword">false</span>)));
        acceptor.setLocalAddress(<span class="code-keyword">new</span> InetSocketAddress(PORT));
        acceptor.setHandler(handler);
        acceptor.bind();
        <span class="code-object">System</span>.out.println(<span class="code-quote">"server is listenig at port "</span> + PORT);
    }
}</pre>
</div></div>
<p>Usage by the client is identical:</p>
<div class="code"><div class="codeContent">
<pre class="code-java"><span class="code-keyword">public</span> class ImageClient <span class="code-keyword">extends</span> IoHandlerAdapter {
    <span class="code-keyword">public</span> <span class="code-keyword">static</span> <span class="code-keyword">final</span> <span class="code-object">int</span> CONNECT_TIMEOUT = 3000;

    <span class="code-keyword">private</span> <span class="code-object">String</span> host;
    <span class="code-keyword">private</span> <span class="code-object">int</span> port;
    <span class="code-keyword">private</span> SocketConnector connector;
    <span class="code-keyword">private</span> IoSession session;
    <span class="code-keyword">private</span> ImageListener imageListener;

    <span class="code-keyword">public</span> ImageClient(<span class="code-object">String</span> host, <span class="code-object">int</span> port, ImageListener imageListener) {
        <span class="code-keyword">this</span>.host = host;
        <span class="code-keyword">this</span>.port = port;
        <span class="code-keyword">this</span>.imageListener = imageListener;
        connector = <span class="code-keyword">new</span> NioSocketConnector();
        connector.getFilterChain().addLast(<span class="code-quote">"codec"</span>, <span class="code-keyword">new</span> ProtocolCodecFilter(<span class="code-keyword">new</span> ImageCodecFactory(<span class="code-keyword">true</span>)));
        connector.setHandler(<span class="code-keyword">this</span>);
    }

    <span class="code-keyword">public</span> void messageReceived(IoSession session, <span class="code-object">Object</span> message) <span class="code-keyword">throws</span> Exception {
        ImageResponse response = (ImageResponse) message;
        imageListener.onImages(response.getImage1(), response.getImage2());
    }
...</pre>
</div></div>
<p>For completeness, I will add the code for the server-side IoHandler:</p>
<div class="code"><div class="codeContent">
<pre class="code-java"><span class="code-keyword">public</span> class ImageServerIoHandler <span class="code-keyword">extends</span> IoHandlerAdapter {

    <span class="code-keyword">private</span> <span class="code-keyword">final</span> <span class="code-keyword">static</span> <span class="code-object">String</span> characters = <span class="code-quote">"mina rocks abcdefghijklmnopqrstuvwxyz0123456789"</span>;

    <span class="code-keyword">public</span> <span class="code-keyword">static</span> <span class="code-keyword">final</span> <span class="code-object">String</span> INDEX_KEY = ImageServerIoHandler.class.getName() + <span class="code-quote">".INDEX"</span>;

    <span class="code-keyword">private</span> Logger logger = LoggerFactory.getLogger(<span class="code-keyword">this</span>.getClass());

    <span class="code-keyword">public</span> void sessionOpened(IoSession session) <span class="code-keyword">throws</span> Exception {
        session.setAttribute(INDEX_KEY, 0);
    }

    <span class="code-keyword">public</span> void exceptionCaught(IoSession session, Throwable cause) <span class="code-keyword">throws</span> Exception {
        IoSessionLogger sessionLogger = IoSessionLogger.getLogger(session, logger);
        sessionLogger.warn(cause.getMessage(), cause);
    }

    <span class="code-keyword">public</span> void messageReceived(IoSession session, <span class="code-object">Object</span> message) <span class="code-keyword">throws</span> Exception {
        ImageRequest request = (ImageRequest) message;
        <span class="code-object">String</span> text1 = generateString(session, request.getNumberOfCharacters());
        <span class="code-object">String</span> text2 = generateString(session, request.getNumberOfCharacters());
        BufferedImage image1 = createImage(request, text1);
        BufferedImage image2 = createImage(request, text2);
        ImageResponse response = <span class="code-keyword">new</span> ImageResponse(image1, image2);
        session.write(response);
    }

    <span class="code-keyword">private</span> BufferedImage createImage(ImageRequest request, <span class="code-object">String</span> text) {
        BufferedImage image = <span class="code-keyword">new</span> BufferedImage(request.getWidth(), request.getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
        Graphics graphics = image.createGraphics();
        graphics.setColor(Color.YELLOW);
        graphics.fillRect(0, 0, image.getWidth(), image.getHeight());
        Font serif = <span class="code-keyword">new</span> Font(<span class="code-quote">"serif"</span>, Font.PLAIN, 30);
        graphics.setFont(serif);
        graphics.setColor(Color.BLUE);
        graphics.drawString(text, 10, 50);
        <span class="code-keyword">return</span> image;
    }

    <span class="code-keyword">private</span> <span class="code-object">String</span> generateString(IoSession session, <span class="code-object">int</span> length) {
        <span class="code-object">Integer</span> index = (<span class="code-object">Integer</span>) session.getAttribute(INDEX_KEY);
        <span class="code-object">StringBuffer</span> buffer = <span class="code-keyword">new</span> <span class="code-object">StringBuffer</span>(length);
        <span class="code-keyword">while</span> (buffer.length() &lt; length) {
            buffer.append(characters.charAt(index));
            index++;
            <span class="code-keyword">if</span> (index &gt;= characters.length()) {
                index = 0;
            }
        }
        session.setAttribute(INDEX_KEY, index);
        <span class="code-keyword">return</span> buffer.toString();
    }

}</pre>
</div></div>
<p>The complete code for this tutorial (basic server + Swing client) can be found <span class="nobr"><a href="/confluence/download/attachments/71339/mina-2.0-codec-tutorial-src.zip?version=1" title="mina-2.0-codec-tutorial-src.zip attached to Tutorial on ProtocolCodecFilter (for Mina 2.x)">here<sup><img class="rendericon" src="/confluence/images/icons/link_attachment_7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span>.</p>

<p><img src="/confluence/download/attachments/54466/screenshot.jpg" align="absmiddle" border="0" /></p>

<h2><a name="TutorialonProtocolCodecFilter%28forMina2.x%29-Conclusion"></a>Conclusion</h2>

<p>There is a lot more to tell about encoding and decoding. But I hope this tutorial already gets you started.<br/>
I will try to add something about the <tt>DemuxingProtocolCodecFactory</tt> in the near future.<br/>
And then we will also have a look at how to use a delimiter instead of a length prefix.</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