tag:blogger.com,1999:blog-24979440317435437662017-08-16T02:29:20.098+02:00Wrong tracks of a developerPosts about the experiences I gather when developing and getting in touch with new technologies. They will include programming languages, tools, version control and so on.Ronny Bräunlichnoreply@blogger.comBlogger40125tag:blogger.com,1999:blog-2497944031743543766.post-48165693075509505762017-06-21T19:36:00.000+02:002017-06-21T19:36:34.160+02:00Gatling Load Testing Part 1 – Using Gatling<style>@charset "UTF-8";@import 'https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.2.0/katex.min.css';code{color:#c7254e;background-color:#f9f2f4;border-radius:4px}code,kbd{padding:2px 4px}kbd{color:#fff;background-color:#333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;box-shadow:none}pre{display:block;margin:0 0 10px;word-break:break-all;word-wrap:break-word;color:#333;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}table{background-color:transparent}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th{padding:8px;line-height:1.4285714;border-top:1px solid #ddd}.table>thead>tr>td,.table>tbody>tr>th,.table>tbody>tr>td,.table>tfoot>tr>th,.table>tfoot>tr>td{padding:8px;line-height:1.4285714;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>th,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered,.table-bordered>thead>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class*="col-"]{position:static;float:none;display:table-column}table td[class*="col-"],table th[class*="col-"]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>thead>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>thead>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>thead>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>thead>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>thead>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}fieldset{border:0;min-width:0}legend{display:block;width:100%;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{padding-top:7px}output,.form-control{display:block;font-size:14px;line-height:1.4285714;color:#555}.form-control{width:100%;height:34px;padding:6px 12px;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#777;opacity:1}.form-control:-ms-input-placeholder{color:#777}.form-control::-webkit-input-placeholder{color:#777}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type="date"],input[type="time"],input[type="datetime-local"],input[type="month"]{line-height:34px;line-height:1.4285714 \0}input[type="date"].input-sm,.form-horizontal .form-group-sm input[type="date"].form-control,.input-group-sm>input[type="date"].form-control,.input-group-sm>input[type="date"].input-group-addon,.input-group-sm>.input-group-btn>input[type="date"].btn,input[type="time"].input-sm,.form-horizontal .form-group-sm input[type="time"].form-control,.input-group-sm>input[type="time"].form-control,.input-group-sm>input[type="time"].input-group-addon,.input-group-sm>.input-group-btn>input[type="time"].btn,input[type="datetime-local"].input-sm,.form-horizontal .form-group-sm input[type="datetime-local"].form-control,.input-group-sm>input[type="datetime-local"].form-control,.input-group-sm>input[type="datetime-local"].input-group-addon,.input-group-sm>.input-group-btn>input[type="datetime-local"].btn,input[type="month"].input-sm,.form-horizontal .form-group-sm input[type="month"].form-control,.input-group-sm>input[type="month"].form-control,.input-group-sm>input[type="month"].input-group-addon,.input-group-sm>.input-group-btn>input[type="month"].btn{line-height:30px}input[type="date"].input-lg,.form-horizontal .form-group-lg input[type="date"].form-control,.input-group-lg>input[type="date"].form-control,.input-group-lg>input[type="date"].input-group-addon,.input-group-lg>.input-group-btn>input[type="date"].btn,input[type="time"].input-lg,.form-horizontal .form-group-lg input[type="time"].form-control,.input-group-lg>input[type="time"].form-control,.input-group-lg>input[type="time"].input-group-addon,.input-group-lg>.input-group-btn>input[type="time"].btn,input[type="datetime-local"].input-lg,.form-horizontal .form-group-lg input[type="datetime-local"].form-control,.input-group-lg>input[type="datetime-local"].form-control,.input-group-lg>input[type="datetime-local"].input-group-addon,.input-group-lg>.input-group-btn>input[type="datetime-local"].btn,input[type="month"].input-lg,.form-horizontal .form-group-lg input[type="month"].form-control,.input-group-lg>input[type="month"].form-control,.input-group-lg>input[type="month"].input-group-addon,.input-group-lg>.input-group-btn>input[type="month"].btn{line-height:46px}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;min-height:20px;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-left:-20px;margin-top:4px \9}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:400;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="radio"].disabled,fieldset[disabled] input[type="radio"],input[type="checkbox"][disabled],input[type="checkbox"].disabled,fieldset[disabled] input[type="checkbox"],.radio-inline.disabled,fieldset[disabled] .radio-inline,.checkbox-inline.disabled,fieldset[disabled] .checkbox-inline,.radio.disabled label,fieldset[disabled] .radio label,.checkbox.disabled label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-horizontal .form-group-lg .form-control-static.form-control,.input-group-lg>.form-control-static.form-control,.input-group-lg>.form-control-static.input-group-addon,.input-group-lg>.input-group-btn>.form-control-static.btn,.form-control-static.input-sm,.form-horizontal .form-group-sm .form-control-static.form-control,.input-group-sm>.form-control-static.form-control,.input-group-sm>.form-control-static.input-group-addon,.input-group-sm>.input-group-btn>.form-control-static.btn{padding-left:0;padding-right:0}.input-sm,.form-horizontal .form-group-sm .form-control,.input-group-sm>.form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.input-group-sm>.input-group-addon{height:30px;line-height:1.5}.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm,.form-horizontal .form-group-sm select.form-control,.input-group-sm>select.form-control,.input-group-sm>select.input-group-addon,.input-group-sm>.input-group-btn>select.btn{height:30px;line-height:30px}textarea.input-sm,.form-horizontal .form-group-sm textarea.form-control,.input-group-sm>textarea.form-control,.input-group-sm>textarea.input-group-addon,.input-group-sm>.input-group-btn>textarea.btn,select[multiple].input-sm,.form-horizontal .form-group-sm select[multiple].form-control,.input-group-sm>select[multiple].form-control,.input-group-sm>select[multiple].input-group-addon,.input-group-sm>.input-group-btn>select[multiple].btn{height:auto}.input-lg,.form-horizontal .form-group-lg .form-control,.input-group-lg>.form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.input-group-lg>.input-group-addon{height:46px;line-height:1.33}.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg,.form-horizontal .form-group-lg select.form-control,.input-group-lg>select.form-control,.input-group-lg>select.input-group-addon,.input-group-lg>.input-group-btn>select.btn{height:46px;line-height:46px}textarea.input-lg,.form-horizontal .form-group-lg textarea.form-control,.input-group-lg>textarea.form-control,.input-group-lg>textarea.input-group-addon,.input-group-lg>.input-group-btn>textarea.btn,select[multiple].input-lg,.form-horizontal .form-group-lg select[multiple].form-control,.input-group-lg>select[multiple].form-control,.input-group-lg>select[multiple].input-group-addon,.input-group-lg>.input-group-btn>select[multiple].btn{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:25px;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center}.input-lg+.form-control-feedback,.form-horizontal .form-group-lg .form-control+.form-control-feedback,.input-group-lg>.form-control+.form-control-feedback,.input-group-lg>.input-group-addon+.form-control-feedback,.input-group-lg>.input-group-btn>.btn+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback,.form-horizontal .form-group-sm .form-control+.form-control-feedback,.input-group-sm>.form-control+.form-control-feedback,.input-group-sm>.input-group-addon+.form-control-feedback,.input-group-sm>.input-group-btn>.btn+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline{color:#3c763d}.has-success .form-control{border-color:#3c763d;box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline{color:#a94442}.has-error .form-control{border-color:#a94442;box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.has-error .form-control-feedback{color:#a94442}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}.form-horizontal .form-group:before{content:" ";display:table}.form-horizontal .form-group:after{content:" ";display:table;clear:both}.form-horizontal .has-feedback .form-control-feedback{top:0;right:15px}.btn{display:inline-block;vertical-align:middle;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;pointer-events:none;opacity:.65;filter:alpha(opacity=65);box-shadow:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active,.open>.btn-default.dropdown-toggle{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.btn-default.dropdown-toggle{background-image:none}.btn-default.disabled,.btn-default.disabled:hover,.btn-default.disabled:focus,.btn-default.disabled:active,.btn-default.disabled.active,.btn-default[disabled],.btn-default[disabled]:hover,.btn-default[disabled]:focus,.btn-default[disabled]:active,.btn-default[disabled].active,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default:hover,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.open>.btn-primary.dropdown-toggle{color:#fff;background-color:#3071a9;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open>.btn-primary.dropdown-toggle{background-image:none}.btn-primary.disabled,.btn-primary.disabled:hover,.btn-primary.disabled:focus,.btn-primary.disabled:active,.btn-primary.disabled.active,.btn-primary[disabled],.btn-primary[disabled]:hover,.btn-primary[disabled]:focus,.btn-primary[disabled]:active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary:hover,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.open>.btn-success.dropdown-toggle{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.btn-success.dropdown-toggle{background-image:none}.btn-success.disabled,.btn-success.disabled:hover,.btn-success.disabled:focus,.btn-success.disabled:active,.btn-success.disabled.active,.btn-success[disabled],.btn-success[disabled]:hover,.btn-success[disabled]:focus,.btn-success[disabled]:active,.btn-success[disabled].active,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success:hover,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open>.btn-info.dropdown-toggle{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.btn-info.dropdown-toggle{background-image:none}.btn-info.disabled,.btn-info.disabled:hover,.btn-info.disabled:focus,.btn-info.disabled:active,.btn-info.disabled.active,.btn-info[disabled],.btn-info[disabled]:hover,.btn-info[disabled]:focus,.btn-info[disabled]:active,.btn-info[disabled].active,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info:hover,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.open>.btn-warning.dropdown-toggle{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.btn-warning.dropdown-toggle{background-image:none}.btn-warning.disabled,.btn-warning.disabled:hover,.btn-warning.disabled:focus,.btn-warning.disabled:active,.btn-warning.disabled.active,.btn-warning[disabled],.btn-warning[disabled]:hover,.btn-warning[disabled]:focus,.btn-warning[disabled]:active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning:hover,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.open>.btn-danger.dropdown-toggle{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.btn-danger.dropdown-toggle{background-image:none}.btn-danger.disabled,.btn-danger.disabled:hover,.btn-danger.disabled:focus,.btn-danger.disabled:active,.btn-danger.disabled.active,.btn-danger[disabled],.btn-danger[disabled]:hover,.btn-danger[disabled]:focus,.btn-danger[disabled]:active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger:hover,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#428bca;font-weight:400;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:hover,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm{padding:5px 10px}.btn-sm,.btn-xs{font-size:12px;line-height:1.5;border-radius:3px}.btn-xs{padding:1px 5px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;transition:height .35s ease}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon{white-space:nowrap}.input-group-addon,.input-group-btn{width:1%;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm,.form-horizontal .form-group-sm .input-group-addon.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.input-group-addon.btn{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg,.form-horizontal .form-group-lg .input-group-addon.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.input-group-addon.btn{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{font-size:0;white-space:nowrap}.input-group-btn,.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.4285714;text-decoration:none;color:#428bca;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:hover,.pagination>li>a:focus,.pagination>li>span:hover,.pagination>li>span:focus{color:#2a6496;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:hover,.pagination>.active>a:focus,.pagination>.active>span,.pagination>.active>span:hover,.pagination>.active>span:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#777;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none}.modal-open,.modal{overflow:hidden}.modal{display:none;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate3d(0,-25%,0);transform:translate3d(0,-25%,0);-webkit-transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,.5);background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5;min-height:16.4285714px}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.4285714}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer:before,.modal-footer:after{content:" ";display:table}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}.clearfix:before,.clearfix:after{content:" ";display:table}.clearfix:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important;visibility:hidden!important}.affix{position:fixed;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.hljs{display:block;overflow-x:auto;padding:.5em;background:#002b36;color:#839496;-webkit-text-size-adjust:none}.hljs-comment,.hljs-template_comment,.diff .hljs-header,.hljs-doctype,.hljs-pi,.lisp .hljs-string,.hljs-javadoc{color:#586e75}.hljs-keyword,.hljs-winutils,.method,.hljs-addition,.css .hljs-tag,.hljs-request,.hljs-status,.nginx .hljs-title{color:#859900}.hljs-number,.hljs-command,.hljs-string,.hljs-tag .hljs-value,.hljs-rules .hljs-value,.hljs-phpdoc,.hljs-dartdoc,.tex .hljs-formula,.hljs-regexp,.hljs-hexcolor,.hljs-link_url{color:#2aa198}.hljs-title,.hljs-localvars,.hljs-chunk,.hljs-decorator,.hljs-built_in,.hljs-identifier,.vhdl .hljs-literal,.hljs-id,.css .hljs-function{color:#268bd2}.hljs-attribute,.hljs-variable,.lisp .hljs-body,.smalltalk .hljs-number,.hljs-constant,.hljs-class .hljs-title,.hljs-parent,.hljs-type,.hljs-link_reference{color:#b58900}.hljs-preprocessor,.hljs-preprocessor .hljs-keyword,.hljs-pragma,.hljs-shebang,.hljs-symbol,.hljs-symbol .hljs-string,.diff .hljs-change,.hljs-special,.hljs-attr_selector,.hljs-subst,.hljs-cdata,.css .hljs-pseudo,.hljs-header{color:#cb4b16}.hljs-deletion,.hljs-important{color:#dc322f}.hljs-link_label{color:#6c71c4}.tex .hljs-formula{background:#073642}*,*:before,*:after{box-sizing:border-box}html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:0 0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}images{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd{font-size:1em}code,kbd,pre,samp{font-family:monospace,monospace}samp{font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}.debug{background-color:#ffc0cb!important}.ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ir{background-color:transparent;border:0;overflow:hidden}.ir::before{content:'';display:block;height:150%;width:0}html{font-size:.875em;background:#fff;color:#373D49}html,body{font-family:Georgia,Cambria,serif;height:100%}body{font-size:1rem;font-weight:400;line-height:2rem}ul,ol{margin-bottom:.83999rem;padding-top:.16001rem}li{-webkit-font-feature-settings:'kern' 1,'onum' 1,'liga' 1;font-feature-settings:'kern' 1,'onum' 1,'liga' 1;margin-left:1rem}li>ul,li>ol{margin-bottom:0}p{padding-top:.66001rem;-webkit-font-feature-settings:'kern' 1,'onum' 1,'liga' 1;font-feature-settings:'kern' 1,'onum' 1,'liga' 1;margin-top:0}p,pre{margin-bottom:1.33999rem}pre{font-size:1rem;padding:.66001rem 9.5px 9.5px;line-height:2rem;background:-webkit-linear-gradient(top,#fff 0,#fff .75rem,#f5f7fa .75rem,#f5f7fa 2.75rem,#fff 2.75rem,#fff 4rem);background:linear-gradient(to bottom,#fff 0,#fff .75rem,#f5f7fa .75rem,#f5f7fa 2.75rem,#fff 2.75rem,#fff 4rem);background-size:100% 4rem;border-color:#D3DAEA}blockquote{margin:0}blockquote p{font-size:1rem;margin-bottom:.33999rem;font-style:italic;padding:.66001rem 1rem 1rem;border-left:3px solid #A0AABF}th,td{padding:12px}h1,h2,h3,h4,h5,h6{font-family:"Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;-webkit-font-feature-settings:'dlig' 1,'liga' 1,'lnum' 1,'kern' 1;font-feature-settings:'dlig' 1,'liga' 1,'lnum' 1,'kern' 1;font-style:normal;font-weight:600;margin-top:0}h1{line-height:3rem;font-size:2.0571429rem;margin-bottom:.21999rem;padding-top:.78001rem}h2{font-size:1.953125rem;margin-bottom:.1835837rem;padding-top:.8164163rem}h2,h3{line-height:3rem}h3{font-size:1.6457143rem;margin-bottom:.07599rem;padding-top:.92401rem}h4{font-size:1.5625rem;margin-bottom:.546865rem;padding-top:.453135rem}h5{font-size:1.25rem;margin-bottom:-.56251rem;padding-top:.56251rem}h6{font-size:1rem;margin-bottom:-.65001rem;padding-top:.65001rem}a{cursor:pointer;color:#35D7BB;text-decoration:none}a:hover,a:focus{border-bottom-color:#35D7BB;color:#dff9f4}img{height:auto;max-width:100%}.g{display:block}.g:after{clear:both;content:'';display:table}.g-b{float:left;margin:0;width:100%}.g{margin-left:-16px;margin-right:-16px}.g-b{padding-left:16px;padding-right:16px}.g-b--center{display:block;float:none;margin:0 auto}.g-b--right{float:right}.g-b--1of1{width:100%}.g-b--1of2,.g-b--2of4,.g-b--3of6,.g-b--4of8,.g-b--5of10,.g-b--6of12{width:50%}.g-b--1of3,.g-b--2of6,.g-b--4of12{width:33.333%}.g-b--2of3,.g-b--4of6,.g-b--8of12{width:66.666%}.g-b--1of4,.g-b--2of8,.g-b--3of12{width:25%}.g-b--3of4,.g-b--6of8,.g-b--9of12{width:75%}.g-b--1of5,.g-b--2of10{width:20%}.g-b--2of5,.g-b--4of10{width:40%}.g-b--3of5,.g-b--6of10{width:60%}.g-b--4of5,.g-b--8of10{width:80%}.g-b--1of6,.g-b--2of12{width:16.666%}.g-b--5of6,.g-b--10of12{width:83.333%}.g-b--1of8{width:12.5%}.g-b--3of8{width:37.5%}.g-b--5of8{width:62.5%}.g-b--7of8{width:87.5%}.g-b--1of10{width:10%}.g-b--3of10{width:30%}.g-b--7of10{width:70%}.g-b--9of10{width:90%}.g-b--1of12{width:8.333%}.g-b--5of12{width:41.666%}.g-b--7of12{width:58.333%}.g-b--11of12{width:91.666%}.g-b--push--1of1{margin-left:100%}.g-b--push--1of2,.g-b--push--2of4,.g-b--push--3of6,.g-b--push--4of8,.g-b--push--5of10,.g-b--push--6of12{margin-left:50%}.g-b--push--1of3,.g-b--push--2of6,.g-b--push--4of12{margin-left:33.333%}.g-b--push--2of3,.g-b--push--4of6,.g-b--push--8of12{margin-left:66.666%}.g-b--push--1of4,.g-b--push--2of8,.g-b--push--3of12{margin-left:25%}.g-b--push--3of4,.g-b--push--6of8,.g-b--push--9of12{margin-left:75%}.g-b--push--1of5,.g-b--push--2of10{margin-left:20%}.g-b--push--2of5,.g-b--push--4of10{margin-left:40%}.g-b--push--3of5,.g-b--push--6of10{margin-left:60%}.g-b--push--4of5,.g-b--push--8of10{margin-left:80%}.g-b--push--1of6,.g-b--push--2of12{margin-left:16.666%}.g-b--push--5of6,.g-b--push--10of12{margin-left:83.333%}.g-b--push--1of8{margin-left:12.5%}.g-b--push--3of8{margin-left:37.5%}.g-b--push--5of8{margin-left:62.5%}.g-b--push--7of8{margin-left:87.5%}.g-b--push--1of10{margin-left:10%}.g-b--push--3of10{margin-left:30%}.g-b--push--7of10{margin-left:70%}.g-b--push--9of10{margin-left:90%}.g-b--push--1of12{margin-left:8.333%}.g-b--push--5of12{margin-left:41.666%}.g-b--push--7of12{margin-left:58.333%}.g-b--push--11of12{margin-left:91.666%}.g-b--pull--1of1{margin-right:100%}.g-b--pull--1of2,.g-b--pull--2of4,.g-b--pull--3of6,.g-b--pull--4of8,.g-b--pull--5of10,.g-b--pull--6of12{margin-right:50%}.g-b--pull--1of3,.g-b--pull--2of6,.g-b--pull--4of12{margin-right:33.333%}.g-b--pull--2of3,.g-b--pull--4of6,.g-b--pull--8of12{margin-right:66.666%}.g-b--pull--1of4,.g-b--pull--2of8,.g-b--pull--3of12{margin-right:25%}.g-b--pull--3of4,.g-b--pull--6of8,.g-b--pull--9of12{margin-right:75%}.g-b--pull--1of5,.g-b--pull--2of10{margin-right:20%}.g-b--pull--2of5,.g-b--pull--4of10{margin-right:40%}.g-b--pull--3of5,.g-b--pull--6of10{margin-right:60%}.g-b--pull--4of5,.g-b--pull--8of10{margin-right:80%}.g-b--pull--1of6,.g-b--pull--2of12{margin-right:16.666%}.g-b--pull--5of6,.g-b--pull--10of12{margin-right:83.333%}.g-b--pull--1of8{margin-right:12.5%}.g-b--pull--3of8{margin-right:37.5%}.g-b--pull--5of8{margin-right:62.5%}.g-b--pull--7of8{margin-right:87.5%}.g-b--pull--1of10{margin-right:10%}.g-b--pull--3of10{margin-right:30%}.g-b--pull--7of10{margin-right:70%}.g-b--pull--9of10{margin-right:90%}.g-b--pull--1of12{margin-right:8.333%}.g-b--pull--5of12{margin-right:41.666%}.g-b--pull--7of12{margin-right:58.333%}.g-b--pull--11of12{margin-right:91.666%}.splashscreen{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#373D49;z-index:22}.splashscreen-dillinger{width:260px;height:auto;display:block;margin:0 auto;padding-bottom:3rem}.splashscreen p{font-size:1.25rem;padding-top:.56251rem;font-family:"Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:400;text-align:center;max-width:500px;margin:0 auto;color:#FFF}.sp-center{position:relative;-webkit-transform:translateY(-50%);transform:translateY(-50%);top:50%}.open-menu>.wrapper{overflow-x:hidden}.page{margin:0 auto;position:relative;top:0;left:0;width:100%;height:100%;z-index:2;-webkit-transition:all .25s ease-in-out;transition:all .25s ease-in-out;background-color:#fff;padding-top:51px;will-change:left}.open-menu .page{left:270px}.title{line-height:1rem;font-size:.8rem;margin-bottom:.77999rem;padding-top:.22001rem;font-weight:500;color:#A0AABF;letter-spacing:1px;text-transform:uppercase;padding-left:16px;padding-right:16px;margin-top:1rem}.split-preview .title{padding-left:0}.title-document{line-height:1rem;font-size:1.25rem;margin-bottom:.89999rem;padding-top:.10001rem;font-weight:400;font-family:"Ubuntu Mono",Monaco;color:#373D49;padding-left:16px;padding-right:16px;width:80%;min-width:300px;outline:0;border:none}.icon{display:block;margin:0 auto;width:36px;height:36px;border-radius:3px;text-align:center}.icon svg{display:inline-block;margin-left:auto;margin-right:auto}.icon-preview{background-color:#373D49;line-height:40px}.icon-preview svg{width:19px;height:12px}.icon-settings{background-color:#373D49;line-height:44px}.icon-settings svg{width:18px;height:18px}.icon-link{width:16px;height:16px;line-height:1;margin-right:24px;text-align:right}.navbar{background-color:#373D49;height:51px;width:100%;position:fixed;top:0;left:0;z-index:6;-webkit-transition:all .25s ease-in-out;transition:all .25s ease-in-out;will-change:left}.navbar:after{content:"";display:table;clear:both}.open-menu .navbar{left:270px}.navbar-brand{float:left;margin:0 0 0 24px;padding:0;line-height:42px}.navbar-brand svg{width:85px;height:11px}.nav-left{float:left}.nav-right{float:right}.nav-sidebar{width:100%}.menu{list-style:none;margin:0;padding:0}.menu a{border:0;color:#A0AABF;font-family:"Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;outline:none;text-transform:uppercase}.menu a:hover{color:#35D7BB}.menu .menu-item{border:0;display:none;float:left;margin:0;position:relative}.menu .menu-item>a{display:block;font-size:12px;height:51px;letter-spacing:1px;line-height:51px;padding:0 24px}.menu .menu-item--settings,.menu .menu-item--preview,.menu .menu-item--save-to.in-sidebar,.menu .menu-item--import-from.in-sidebar,.menu .menu-item--link-unlink.in-sidebar,.menu .menu-item--documents.in-sidebar{display:block}.menu .menu-item--documents{padding-bottom:1rem}.menu .menu-item.open>a{background-color:#1D212A}.menu .menu-item-icon>a{height:auto;padding:0}.menu .menu-item-icon:hover>a{background-color:transparent}.menu .menu-link.open i{background-color:#1D212A}.menu .menu-link.open g{fill:#35D7BB}.menu .menu-link-preview,.menu .menu-link-settings{margin-top:8px;width:51px}.menu-sidebar{width:100%}.menu-sidebar .menu-item{float:none;margin-bottom:1px;width:100%}.menu-sidebar .menu-item.open>a{background-color:#373D49}.menu-sidebar .open .caret{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.menu-sidebar>.menu-item:hover .dropdown a,.menu-sidebar>.menu-item:hover .settings a{background-color:transparent}.menu-sidebar .menu-link{background-color:#373D49;font-weight:600}.menu-sidebar .menu-link:after{content:"";display:table;clear:both}.menu-sidebar .menu-link>span{float:left}.menu-sidebar .menu-link>.caret{float:right;text-align:right;top:22px}.menu-sidebar .dropdown,.menu-sidebar .settings{background-color:transparent;position:static;width:100%}.dropdown{position:absolute;right:0;top:51px;width:188px}.dropdown,.settings{display:none;background-color:#1D212A}.dropdown{padding:0}.dropdown,.settings,.sidebar-list{list-style:none;margin:0}.sidebar-list{padding:0}.dropdown li{margin:32px 0;padding:0 0 0 32px}.dropdown li,.settings li{line-height:1}.sidebar-list li{line-height:1;margin:32px 0;padding:0 0 0 32px}.dropdown a{color:#D0D6E2}.dropdown a,.settings a,.sidebar-list a{display:block;text-transform:none}.sidebar-list a{color:#D0D6E2}.dropdown a:after,.settings a:after,.sidebar-list a:after{content:"";display:table;clear:both}.dropdown .icon,.settings .icon,.sidebar-list .icon{float:right}.open .dropdown,.open .settings,.open .sidebar-list{display:block}.open .dropdown.collapse,.open .collapse.settings,.open .sidebar-list.collapse{display:none}.open .dropdown.collapse.in,.open .collapse.in.settings,.open .sidebar-list.collapse.in{display:block}.dropdown .unlinked .icon,.settings .unlinked .icon,.sidebar-list .unlinked .icon{opacity:.3}.dropdown.documents li,.documents.settings li,.sidebar-list.documents li{background-image:url("../img/icons/file.svg");background-position:240px center;background-repeat:no-repeat;background-size:14px 16px;padding:3px 32px}.dropdown.documents li.octocat,.documents.settings li.octocat,.sidebar-list.documents li.octocat{background-image:url("../img/icons/octocat.svg");background-position:234px center;background-size:24px 24px}.dropdown.documents li:last-child,.documents.settings li:last-child,.sidebar-list.documents li:last-child{margin-bottom:1rem}.dropdown.documents li.active a,.documents.settings li.active a,.sidebar-list.documents li.active a{color:#35D7BB}.settings{position:fixed;top:67px;right:16px;border-radius:3px;width:288px;background-color:#373D49;padding:16px;z-index:7}.show-settings .settings{display:block}.settings .has-checkbox{float:left}.settings a{font-size:1.25rem;font-family:"Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:400;-webkit-font-smoothing:antialiased;line-height:28px;color:#D0D6E2}.settings a:after{content:"";display:table;clear:both}.settings a:hover{color:#35D7BB}.settings li{border-bottom:1px solid #4F535B;margin:0;padding:16px 0}.settings li:last-child{border-bottom:none}.brand{border:none;display:block}.brand:hover g{fill:#35D7BB}.toggle{display:block;float:left;height:16px;padding:25px 16px 26px;width:40px}.toggle span:after,.toggle span:before{content:'';left:0;position:absolute;top:-6px}.toggle span:after{top:6px}.toggle span{display:block;position:relative}.toggle span,.toggle span:after,.toggle span:before{-webkit-backface-visibility:hidden;backface-visibility:hidden;background-color:#D3DAEA;height:2px;-webkit-transition:all .3s;transition:all .3s;width:20px}.open-menu .toggle span{background-color:transparent}.open-menu .toggle span:before{-webkit-transform:rotate(45deg) translate(3px,3px);transform:rotate(45deg) translate(3px,3px)}.open-menu .toggle span:after{-webkit-transform:rotate(-45deg) translate(5px,-6px);transform:rotate(-45deg) translate(5px,-6px)}.caret{display:inline-block;width:0;height:0;margin-left:6px;vertical-align:middle;position:relative;top:-1px;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.sidebar{overflow:auto;height:100%;padding-right:15px;padding-bottom:15px;width:285px}.sidebar-wrapper{-webkit-overflow-scrolling:touch;background-color:#2B2F36;left:0;height:100%;overflow-y:hidden;position:fixed;top:0;width:285px;z-index:1}.sidebar-branding{width:160px;padding:0;margin:16px auto}.header{border-bottom:1px solid #E8E8E8;position:relative}.words{line-height:1rem;font-size:.8rem;margin-bottom:.77999rem;padding-top:.22001rem;font-weight:500;font-family:"Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;color:#A0AABF;letter-spacing:1px;text-transform:uppercase;z-index:5;position:absolute;right:16px;top:0}.words span{color:#000}.btn{text-align:center;display:inline-block;width:100%;text-transform:uppercase;font-weight:600;font-family:"Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;text-shadow:0 1px 0 #1b8b77;padding:16px 24px;background-color:#35D7BB;border-radius:3px;margin:0 auto 16px;line-height:1;color:#fff;-webkit-transition:all .15s linear;transition:all .15s linear;-webkit-font-smoothing:antialiased}.btn--new,.btn--save{display:block;width:238px}.btn--new:hover,.btn--new:focus,.btn--save:hover,.btn--save:focus{color:#fff;border-bottom-color:transparent;box-shadow:0 1px 3px #24b59c;text-shadow:0 1px 0 #24b59c}.btn--save{background-color:#4A5261;text-shadow:0 1px 1px #1e2127}.btn--save:hover,.btn--save:focus{color:#fff;border-bottom-color:transparent;box-shadow:0 1px 5px #08090a;text-shadow:none}.btn--delete{display:block;width:238px;background-color:transparent;font-size:12px;text-shadow:none}.btn--delete:hover,.btn--delete:focus{color:#fff;border-bottom-color:transparent;text-shadow:0 1px 0 #08090a;opacity:.8}.btn--delete-modal,.btn--ok,.btn--close{border-top:0;background-color:#4A5261;text-shadow:0 1px 0 #08090a;margin:0}.btn--delete-modal:hover,.btn--delete-modal:focus,.btn--ok:hover,.btn--ok:focus,.btn--close:hover,.btn--close:focus{color:#fff;background-color:#292d36;text-shadow:none}.btn--delete-modal{display:inline;width:auto}.overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:rgba(55,61,73,.8);-webkit-transition:all .25s ease-in-out;transition:all .25s ease-in-out;-webkit-transition-timing-function:ease-out;transition-timing-function:ease-out;will-change:left,opacity,visibility;z-index:5;opacity:0;visibility:hidden}.show-settings .overlay{visibility:visible;opacity:1}.switch{float:right;line-height:1}.switch input{display:none}.switch small{display:inline-block;cursor:pointer;padding:0 24px 0 0;-webkit-transition:all ease .2s;transition:all ease .2s;background-color:#2B2F36;border-color:#2B2F36}.switch small,.switch small:before{border-radius:30px;box-shadow:inset 0 0 2px 0 #14171F}.switch small:before{display:block;content:'';width:28px;height:28px;background:#fff}.switch.checked small{padding-right:0;padding-left:24px;background-color:#35D7BB;box-shadow:none}.modal--dillinger.about .modal-dialog{font-size:1.25rem;max-width:500px}.modal--dillinger.scope .modal-dialog{max-width:300px;margin:5rem auto}.modal--dillinger .modal-dialog{max-width:600px;width:auto;margin:5rem auto}.modal--dillinger .modal-content{background:#373D49;border-radius:3px;box-shadow:0 2px 5px 0 #2C3B59;color:#fff;font-family:"Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:400;padding:2rem}.modal--dillinger ul{list-style-type:disc;margin:1rem 0;padding:0 0 0 1rem}.modal--dillinger li{padding:0;margin:0}.modal--dillinger .modal-header{border:0;padding:0}.modal--dillinger .modal-body{padding:0}.modal--dillinger .modal-footer{border:0;padding:0}.modal--dillinger .close{color:#fff;opacity:1}.modal-backdrop{background-color:#373D49}.pagination--dillinger{padding:0!important;margin:1.5rem 0!important;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:stretch;align-content:stretch}.pagination--dillinger,.pagination--dillinger li{display:-webkit-box;display:-ms-flexbox;display:flex}.pagination--dillinger li{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;text-align:center}.pagination--dillinger li:first-child>a,.pagination--dillinger li.disabled>a,.pagination--dillinger li.disabled>a:hover,.pagination--dillinger li.disabled>a:focus,.pagination--dillinger li>a{background-color:transparent;border-color:#4F535B;border-right-color:transparent}.pagination--dillinger li.active>a,.pagination--dillinger li.active>a:hover,.pagination--dillinger li.active>a:focus{border-color:#4A5261;background-color:#4A5261;color:#fff}.pagination--dillinger li>a{float:none;color:#fff;width:100%;display:block;text-align:center;margin:0;border-right-color:transparent;padding:6px}.pagination--dillinger li>a:hover,.pagination--dillinger li>a:focus{border-color:#35D7BB;background-color:#35D7BB;color:#fff}.pagination--dillinger li:last-child a{border-color:#4F535B}.pagination--dillinger li:first-child a{border-right-color:transparent}.diNotify{position:absolute;z-index:9999;left:0;right:0;top:0;margin:0 auto;max-width:400px;text-align:center;-webkit-transition:top .5s ease-in-out,opacity .5s ease-in-out;transition:top .5s ease-in-out,opacity .5s ease-in-out;visibility:hidden}.diNotify-body{-webkit-font-smoothing:antialiased;background-color:#35D7BB;background:#666E7F;border-radius:3px;color:#fff;font-family:"Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:400;overflow:hidden;padding:1rem 2rem .5rem;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:baseline;-ms-flex-align:baseline;align-items:baseline;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.diNotify-icon{display:block;width:16px;height:16px;line-height:16px;position:relative;top:3px}.diNotify-message{padding-left:1rem}.zen-wrapper{position:fixed;top:0;left:0;right:0;bottom:0;width:100%;height:100%;z-index:10;background-color:#FFF;opacity:0;-webkit-transition:opacity .25s ease-in-out;transition:opacity .25s ease-in-out}.zen-wrapper.on{opacity:1}.enter-zen-mode{background-image:url("../img/icons/enter-zen.svg");right:.5rem;top:.313rem;display:none}.enter-zen-mode,.close-zen-mode{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0;background-repeat:no-repeat;width:32px;height:32px;display:block;position:absolute}.close-zen-mode{background-image:url("../img/icons/exit-zen.svg");right:1rem;top:1rem}.zen-page{position:relative;top:0;bottom:0;z-index:11;height:100%;width:100%}#zen{font-size:1.25rem;width:300px;height:80%;margin:0 auto;position:relative;top:10%}#zen:before,#zen:after{content:"";position:absolute;height:10%;width:100%;z-index:12;pointer-events:none}#preview .table{width:auto}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:.1px;z-index:99999;display:block}.ui-resizable-e{background-color:#666;border-right:8px solid #e8e8e8;border-left:1px solid #222;width:10px;z-index:88!important;position:relative}.ui-resizable-e:after{content:"-";display:block;position:absolute;top:calc(50% - 16px);left:0;height:25px;width:2px;background-color:rgba(0,0,0,.4);margin:3px}#editor{cursor:ew-resize;position:relative;z-index:auto}.profile-pic{float:left;width:250px}#_default_ a::before{color:#A0AABF}#_default_ img{display:none}#_default_ #_default_{display:block;float:left;max-width:38%;word-wrap:break-word}#_default_ .default-ad{display:none}#_default_ ._default_{display:block}#_default_ a{color:#35d7bb;text-decoration:none}#_default_ a:hover{color:#8ae8d8}#_default_ .default-image{display:none}#_default_ .default-title:after{content:" — "}#_default_ .default-title,#_default_ .default-description{display:inline}#_default_ .default-title{position:relative;font-weight:600;display:none}#_default_ a:before{position:relative;top:0;padding:5px;color:#a0aabf;content:"Ad";text-transform:uppercase;font-size:8px;font-family:Verdana,sans-serif}#_default_{display:block;float:left;max-width:38%;word-wrap:break-word}#_default_ ._default_{display:block;font-size:.75rem;height:51px;letter-spacing:1px;line-height:1rem;padding:18px 24px}.split{overflow:scroll;padding:0!important}.split-editor{padding-left:0;padding-right:0;position:relative;z-index:3}.show-preview .split-editor{display:none}.split-preview{background-color:#fff;display:none;top:0;position:relative;z-index:4}.show-preview .split-preview{display:block}#editor{font-size:1rem;font-family:"Ubuntu Mono",Monaco;font-weight:400;line-height:2rem;width:100%;height:100%}#editor .ace_gutter{-webkit-font-smoothing:antialiased}.editor-header{width:50%;float:left;border-bottom:1px solid #E8E8E8;position:relative}.editor-header--first{border-right:1px solid #E8E8E8}.editor-header .title{display:inline-block}#preview{padding:15px}#preview a{color:#A0AABF;text-decoration:underline}.sr-only{visibility:hidden;text-overflow:110%;overflow:hidden;top:-100px;position:absolute}.mnone{margin:0!important}@media screen and (min-width:27.5em){html{font-size:.875em}body{font-size:1rem}ul,ol{margin-bottom:.83999rem;padding-top:.16001rem}p{padding-top:.66001rem}p,pre{margin-bottom:1.33999rem}pre,blockquote p{font-size:1rem;padding-top:.66001rem}blockquote p{margin-bottom:.33999rem}h1{font-size:2.0571429rem;margin-bottom:.21999rem;padding-top:.78001rem}h2{font-size:1.953125rem;margin-bottom:.1835837rem;padding-top:.8164163rem}h3{font-size:1.6457143rem;margin-bottom:.07599rem;padding-top:.92401rem}h4{font-size:1.5625rem;margin-bottom:.546865rem;padding-top:.453135rem}h5{font-size:1.25rem;margin-bottom:-.56251rem;padding-top:.56251rem}h6{font-size:1rem;margin-bottom:-.65001rem;padding-top:.65001rem}.g{margin-left:-16px;margin-right:-16px}.g-b{padding-left:16px;padding-right:16px}.g-b--m1of1{width:100%}.g-b--m1of2,.g-b--m2of4,.g-b--m3of6,.g-b--m4of8,.g-b--m5of10,.g-b--m6of12{width:50%}.g-b--m1of3,.g-b--m2of6,.g-b--m4of12{width:33.333%}.g-b--m2of3,.g-b--m4of6,.g-b--m8of12{width:66.666%}.g-b--m1of4,.g-b--m2of8,.g-b--m3of12{width:25%}.g-b--m3of4,.g-b--m6of8,.g-b--m9of12{width:75%}.g-b--m1of5,.g-b--m2of10{width:20%}.g-b--m2of5,.g-b--m4of10{width:40%}.g-b--m3of5,.g-b--m6of10{width:60%}.g-b--m4of5,.g-b--m8of10{width:80%}.g-b--m1of6,.g-b--m2of12{width:16.666%}.g-b--m5of6,.g-b--m10of12{width:83.333%}.g-b--m1of8{width:12.5%}.g-b--m3of8{width:37.5%}.g-b--m5of8{width:62.5%}.g-b--m7of8{width:87.5%}.g-b--m1of10{width:10%}.g-b--m3of10{width:30%}.g-b--m7of10{width:70%}.g-b--m9of10{width:90%}.g-b--m1of12{width:8.333%}.g-b--m5of12{width:41.666%}.g-b--m7of12{width:58.333%}.g-b--m11of12{width:91.666%}.g-b--push--m1of1{margin-left:100%}.g-b--push--m1of2,.g-b--push--m2of4,.g-b--push--m3of6,.g-b--push--m4of8,.g-b--push--m5of10,.g-b--push--m6of12{margin-left:50%}.g-b--push--m1of3,.g-b--push--m2of6,.g-b--push--m4of12{margin-left:33.333%}.g-b--push--m2of3,.g-b--push--m4of6,.g-b--push--m8of12{margin-left:66.666%}.g-b--push--m1of4,.g-b--push--m2of8,.g-b--push--m3of12{margin-left:25%}.g-b--push--m3of4,.g-b--push--m6of8,.g-b--push--m9of12{margin-left:75%}.g-b--push--m1of5,.g-b--push--m2of10{margin-left:20%}.g-b--push--m2of5,.g-b--push--m4of10{margin-left:40%}.g-b--push--m3of5,.g-b--push--m6of10{margin-left:60%}.g-b--push--m4of5,.g-b--push--m8of10{margin-left:80%}.g-b--push--m1of6,.g-b--push--m2of12{margin-left:16.666%}.g-b--push--m5of6,.g-b--push--m10of12{margin-left:83.333%}.g-b--push--m1of8{margin-left:12.5%}.g-b--push--m3of8{margin-left:37.5%}.g-b--push--m5of8{margin-left:62.5%}.g-b--push--m7of8{margin-left:87.5%}.g-b--push--m1of10{margin-left:10%}.g-b--push--m3of10{margin-left:30%}.g-b--push--m7of10{margin-left:70%}.g-b--push--m9of10{margin-left:90%}.g-b--push--m1of12{margin-left:8.333%}.g-b--push--m5of12{margin-left:41.666%}.g-b--push--m7of12{margin-left:58.333%}.g-b--push--m11of12{margin-left:91.666%}.g-b--pull--m1of1{margin-right:100%}.g-b--pull--m1of2,.g-b--pull--m2of4,.g-b--pull--m3of6,.g-b--pull--m4of8,.g-b--pull--m5of10,.g-b--pull--m6of12{margin-right:50%}.g-b--pull--m1of3,.g-b--pull--m2of6,.g-b--pull--m4of12{margin-right:33.333%}.g-b--pull--m2of3,.g-b--pull--m4of6,.g-b--pull--m8of12{margin-right:66.666%}.g-b--pull--m1of4,.g-b--pull--m2of8,.g-b--pull--m3of12{margin-right:25%}.g-b--pull--m3of4,.g-b--pull--m6of8,.g-b--pull--m9of12{margin-right:75%}.g-b--pull--m1of5,.g-b--pull--m2of10{margin-right:20%}.g-b--pull--m2of5,.g-b--pull--m4of10{margin-right:40%}.g-b--pull--m3of5,.g-b--pull--m6of10{margin-right:60%}.g-b--pull--m4of5,.g-b--pull--m8of10{margin-right:80%}.g-b--pull--m1of6,.g-b--pull--m2of12{margin-right:16.666%}.g-b--pull--m5of6,.g-b--pull--m10of12{margin-right:83.333%}.g-b--pull--m1of8{margin-right:12.5%}.g-b--pull--m3of8{margin-right:37.5%}.g-b--pull--m5of8{margin-right:62.5%}.g-b--pull--m7of8{margin-right:87.5%}.g-b--pull--m1of10{margin-right:10%}.g-b--pull--m3of10{margin-right:30%}.g-b--pull--m7of10{margin-right:70%}.g-b--pull--m9of10{margin-right:90%}.g-b--pull--m1of12{margin-right:8.333%}.g-b--pull--m5of12{margin-right:41.666%}.g-b--pull--m7of12{margin-right:58.333%}.g-b--pull--m11of12{margin-right:91.666%}.splashscreen p{font-size:1.25rem;margin-bottom:1.43749rem;padding-top:.56251rem}.title{font-size:.8rem;margin-bottom:.77999rem;padding-top:.22001rem}.title-document{margin-bottom:.89999rem;padding-top:.10001rem}.title-document,.settings a{font-size:1.25rem}.words{font-size:.8rem;margin-bottom:.77999rem;padding-top:.22001rem}.modal--dillinger.about .modal-dialog,#zen{font-size:1.25rem}#zen{width:400px}#editor{font-size:1rem}}@media screen and (min-width:46.25em){html{font-size:.875em}body{font-size:1rem}ul,ol{margin-bottom:.83999rem;padding-top:.16001rem}p{padding-top:.66001rem}p,pre{margin-bottom:1.33999rem}pre,blockquote p{font-size:1rem;padding-top:.66001rem}blockquote p{margin-bottom:.33999rem}h1{font-size:2.0571429rem;margin-bottom:.21999rem;padding-top:.78001rem}h2{font-size:1.953125rem;margin-bottom:.1835837rem;padding-top:.8164163rem}h3{font-size:1.6457143rem;margin-bottom:.07599rem;padding-top:.92401rem}h4{font-size:1.5625rem;margin-bottom:.546865rem;padding-top:.453135rem}h5{font-size:1.25rem;margin-bottom:-.56251rem;padding-top:.56251rem}h6{font-size:1rem;margin-bottom:-.65001rem;padding-top:.65001rem}.g{margin-left:-16px;margin-right:-16px}.g-b{padding-left:16px;padding-right:16px}.g-b--t1of1{width:100%}.g-b--t1of2,.g-b--t2of4,.g-b--t3of6,.g-b--t4of8,.g-b--t5of10,.g-b--t6of12{width:50%}.g-b--t1of3,.g-b--t2of6,.g-b--t4of12{width:33.333%}.g-b--t2of3,.g-b--t4of6,.g-b--t8of12{width:66.666%}.g-b--t1of4,.g-b--t2of8,.g-b--t3of12{width:25%}.g-b--t3of4,.g-b--t6of8,.g-b--t9of12{width:75%}.g-b--t1of5,.g-b--t2of10{width:20%}.g-b--t2of5,.g-b--t4of10{width:40%}.g-b--t3of5,.g-b--t6of10{width:60%}.g-b--t4of5,.g-b--t8of10{width:80%}.g-b--t1of6,.g-b--t2of12{width:16.666%}.g-b--t5of6,.g-b--t10of12{width:83.333%}.g-b--t1of8{width:12.5%}.g-b--t3of8{width:37.5%}.g-b--t5of8{width:62.5%}.g-b--t7of8{width:87.5%}.g-b--t1of10{width:10%}.g-b--t3of10{width:30%}.g-b--t7of10{width:70%}.g-b--t9of10{width:90%}.g-b--t1of12{width:8.333%}.g-b--t5of12{width:41.666%}.g-b--t7of12{width:58.333%}.g-b--t11of12{width:91.666%}.g-b--push--t1of1{margin-left:100%}.g-b--push--t1of2,.g-b--push--t2of4,.g-b--push--t3of6,.g-b--push--t4of8,.g-b--push--t5of10,.g-b--push--t6of12{margin-left:50%}.g-b--push--t1of3,.g-b--push--t2of6,.g-b--push--t4of12{margin-left:33.333%}.g-b--push--t2of3,.g-b--push--t4of6,.g-b--push--t8of12{margin-left:66.666%}.g-b--push--t1of4,.g-b--push--t2of8,.g-b--push--t3of12{margin-left:25%}.g-b--push--t3of4,.g-b--push--t6of8,.g-b--push--t9of12{margin-left:75%}.g-b--push--t1of5,.g-b--push--t2of10{margin-left:20%}.g-b--push--t2of5,.g-b--push--t4of10{margin-left:40%}.g-b--push--t3of5,.g-b--push--t6of10{margin-left:60%}.g-b--push--t4of5,.g-b--push--t8of10{margin-left:80%}.g-b--push--t1of6,.g-b--push--t2of12{margin-left:16.666%}.g-b--push--t5of6,.g-b--push--t10of12{margin-left:83.333%}.g-b--push--t1of8{margin-left:12.5%}.g-b--push--t3of8{margin-left:37.5%}.g-b--push--t5of8{margin-left:62.5%}.g-b--push--t7of8{margin-left:87.5%}.g-b--push--t1of10{margin-left:10%}.g-b--push--t3of10{margin-left:30%}.g-b--push--t7of10{margin-left:70%}.g-b--push--t9of10{margin-left:90%}.g-b--push--t1of12{margin-left:8.333%}.g-b--push--t5of12{margin-left:41.666%}.g-b--push--t7of12{margin-left:58.333%}.g-b--push--t11of12{margin-left:91.666%}.g-b--pull--t1of1{margin-right:100%}.g-b--pull--t1of2,.g-b--pull--t2of4,.g-b--pull--t3of6,.g-b--pull--t4of8,.g-b--pull--t5of10,.g-b--pull--t6of12{margin-right:50%}.g-b--pull--t1of3,.g-b--pull--t2of6,.g-b--pull--t4of12{margin-right:33.333%}.g-b--pull--t2of3,.g-b--pull--t4of6,.g-b--pull--t8of12{margin-right:66.666%}.g-b--pull--t1of4,.g-b--pull--t2of8,.g-b--pull--t3of12{margin-right:25%}.g-b--pull--t3of4,.g-b--pull--t6of8,.g-b--pull--t9of12{margin-right:75%}.g-b--pull--t1of5,.g-b--pull--t2of10{margin-right:20%}.g-b--pull--t2of5,.g-b--pull--t4of10{margin-right:40%}.g-b--pull--t3of5,.g-b--pull--t6of10{margin-right:60%}.g-b--pull--t4of5,.g-b--pull--t8of10{margin-right:80%}.g-b--pull--t1of6,.g-b--pull--t2of12{margin-right:16.666%}.g-b--pull--t5of6,.g-b--pull--t10of12{margin-right:83.333%}.g-b--pull--t1of8{margin-right:12.5%}.g-b--pull--t3of8{margin-right:37.5%}.g-b--pull--t5of8{margin-right:62.5%}.g-b--pull--t7of8{margin-right:87.5%}.g-b--pull--t1of10{margin-right:10%}.g-b--pull--t3of10{margin-right:30%}.g-b--pull--t7of10{margin-right:70%}.g-b--pull--t9of10{margin-right:90%}.g-b--pull--t1of12{margin-right:8.333%}.g-b--pull--t5of12{margin-right:41.666%}.g-b--pull--t7of12{margin-right:58.333%}.g-b--pull--t11of12{margin-right:91.666%}.splashscreen-dillinger{width:500px}.splashscreen p{font-size:1.25rem;margin-bottom:1.43749rem;padding-top:.56251rem}.title{font-size:.8rem;margin-bottom:.77999rem;padding-top:.22001rem}.title-document{font-size:1.25rem;margin-bottom:.89999rem;padding-top:.10001rem}.menu .menu-item--save-to,.menu .menu-item--import-from{display:block}.menu .menu-item--preview,.menu .menu-item--save-to.in-sidebar,.menu .menu-item--import-from.in-sidebar{display:none}.settings a{font-size:1.25rem}.words{font-size:.8rem;margin-bottom:.77999rem;padding-top:.22001rem}.modal--dillinger.about .modal-dialog{font-size:1.25rem}.enter-zen-mode{display:block}.close-zen-mode{right:3rem;top:3rem}#zen{font-size:1.25rem;width:500px}.split-editor{border-right:1px solid #E8E8E8;float:left;height:calc(100vh - 172px);-webkit-overflow-scrolling:touch;padding-right:16px;width:50%}.show-preview .split-editor{display:block}.split-preview{display:block;float:right;height:calc(100vh - 172px);-webkit-overflow-scrolling:touch;position:relative;top:0;width:50%}#editor{font-size:1rem}}@media screen and (min-width:62.5em){html{font-size:.875em}body{font-size:1rem}ul,ol{margin-bottom:.83999rem;padding-top:.16001rem}p{padding-top:.66001rem}p,pre{margin-bottom:1.33999rem}pre,blockquote p{font-size:1rem;padding-top:.66001rem}blockquote p{margin-bottom:.33999rem}h1{font-size:2.0571429rem;margin-bottom:.21999rem;padding-top:.78001rem}h2{font-size:1.953125rem;margin-bottom:.1835837rem;padding-top:.8164163rem}h3{font-size:1.6457143rem;margin-bottom:.07599rem;padding-top:.92401rem}h4{font-size:1.5625rem;margin-bottom:.546865rem;padding-top:.453135rem}h5{font-size:1.25rem;margin-bottom:-.56251rem;padding-top:.56251rem}h6{font-size:1rem;margin-bottom:-.65001rem;padding-top:.65001rem}.g{margin-left:-16px;margin-right:-16px}.g-b{padding-left:16px;padding-right:16px}.g-b--d1of1{width:100%}.g-b--d1of2,.g-b--d2of4,.g-b--d3of6,.g-b--d4of8,.g-b--d5of10,.g-b--d6of12{width:50%}.g-b--d1of3,.g-b--d2of6,.g-b--d4of12{width:33.333%}.g-b--d2of3,.g-b--d4of6,.g-b--d8of12{width:66.666%}.g-b--d1of4,.g-b--d2of8,.g-b--d3of12{width:25%}.g-b--d3of4,.g-b--d6of8,.g-b--d9of12{width:75%}.g-b--d1of5,.g-b--d2of10{width:20%}.g-b--d2of5,.g-b--d4of10{width:40%}.g-b--d3of5,.g-b--d6of10{width:60%}.g-b--d4of5,.g-b--d8of10{width:80%}.g-b--d1of6,.g-b--d2of12{width:16.666%}.g-b--d5of6,.g-b--d10of12{width:83.333%}.g-b--d1of8{width:12.5%}.g-b--d3of8{width:37.5%}.g-b--d5of8{width:62.5%}.g-b--d7of8{width:87.5%}.g-b--d1of10{width:10%}.g-b--d3of10{width:30%}.g-b--d7of10{width:70%}.g-b--d9of10{width:90%}.g-b--d1of12{width:8.333%}.g-b--d5of12{width:41.666%}.g-b--d7of12{width:58.333%}.g-b--d11of12{width:91.666%}.g-b--push--d1of1{margin-left:100%}.g-b--push--d1of2,.g-b--push--d2of4,.g-b--push--d3of6,.g-b--push--d4of8,.g-b--push--d5of10,.g-b--push--d6of12{margin-left:50%}.g-b--push--d1of3,.g-b--push--d2of6,.g-b--push--d4of12{margin-left:33.333%}.g-b--push--d2of3,.g-b--push--d4of6,.g-b--push--d8of12{margin-left:66.666%}.g-b--push--d1of4,.g-b--push--d2of8,.g-b--push--d3of12{margin-left:25%}.g-b--push--d3of4,.g-b--push--d6of8,.g-b--push--d9of12{margin-left:75%}.g-b--push--d1of5,.g-b--push--d2of10{margin-left:20%}.g-b--push--d2of5,.g-b--push--d4of10{margin-left:40%}.g-b--push--d3of5,.g-b--push--d6of10{margin-left:60%}.g-b--push--d4of5,.g-b--push--d8of10{margin-left:80%}.g-b--push--d1of6,.g-b--push--d2of12{margin-left:16.666%}.g-b--push--d5of6,.g-b--push--d10of12{margin-left:83.333%}.g-b--push--d1of8{margin-left:12.5%}.g-b--push--d3of8{margin-left:37.5%}.g-b--push--d5of8{margin-left:62.5%}.g-b--push--d7of8{margin-left:87.5%}.g-b--push--d1of10{margin-left:10%}.g-b--push--d3of10{margin-left:30%}.g-b--push--d7of10{margin-left:70%}.g-b--push--d9of10{margin-left:90%}.g-b--push--d1of12{margin-left:8.333%}.g-b--push--d5of12{margin-left:41.666%}.g-b--push--d7of12{margin-left:58.333%}.g-b--push--d11of12{margin-left:91.666%}.g-b--pull--d1of1{margin-right:100%}.g-b--pull--d1of2,.g-b--pull--d2of4,.g-b--pull--d3of6,.g-b--pull--d4of8,.g-b--pull--d5of10,.g-b--pull--d6of12{margin-right:50%}.g-b--pull--d1of3,.g-b--pull--d2of6,.g-b--pull--d4of12{margin-right:33.333%}.g-b--pull--d2of3,.g-b--pull--d4of6,.g-b--pull--d8of12{margin-right:66.666%}.g-b--pull--d1of4,.g-b--pull--d2of8,.g-b--pull--d3of12{margin-right:25%}.g-b--pull--d3of4,.g-b--pull--d6of8,.g-b--pull--d9of12{margin-right:75%}.g-b--pull--d1of5,.g-b--pull--d2of10{margin-right:20%}.g-b--pull--d2of5,.g-b--pull--d4of10{margin-right:40%}.g-b--pull--d3of5,.g-b--pull--d6of10{margin-right:60%}.g-b--pull--d4of5,.g-b--pull--d8of10{margin-right:80%}.g-b--pull--d1of6,.g-b--pull--d2of12{margin-right:16.666%}.g-b--pull--d5of6,.g-b--pull--d10of12{margin-right:83.333%}.g-b--pull--d1of8{margin-right:12.5%}.g-b--pull--d3of8{margin-right:37.5%}.g-b--pull--d5of8{margin-right:62.5%}.g-b--pull--d7of8{margin-right:87.5%}.g-b--pull--d1of10{margin-right:10%}.g-b--pull--d3of10{margin-right:30%}.g-b--pull--d7of10{margin-right:70%}.g-b--pull--d9of10{margin-right:90%}.g-b--pull--d1of12{margin-right:8.333%}.g-b--pull--d5of12{margin-right:41.666%}.g-b--pull--d7of12{margin-right:58.333%}.g-b--pull--d11of12{margin-right:91.666%}.splashscreen-dillinger{width:700px}.splashscreen p{font-size:1.25rem;margin-bottom:1.43749rem;padding-top:.56251rem}.title{font-size:.8rem;margin-bottom:.77999rem;padding-top:.22001rem}.title-document{font-size:1.25rem;margin-bottom:.89999rem;padding-top:.10001rem}.menu .menu-item--export-as{display:block}.menu .menu-item--preview{display:none}.settings a{font-size:1.25rem}.words{font-size:.8rem;margin-bottom:.77999rem;padding-top:.22001rem}.modal--dillinger.about .modal-dialog,#zen{font-size:1.25rem}#zen{width:700px}#editor{font-size:1rem}}@media screen and (min-width:87.5em){html{font-size:.875em}body{font-size:1rem}ul,ol{margin-bottom:.83999rem;padding-top:.16001rem}p{padding-top:.66001rem}p,pre{margin-bottom:1.33999rem}pre,blockquote p{font-size:1rem;padding-top:.66001rem}blockquote p{margin-bottom:.33999rem}h1{font-size:2.0571429rem;margin-bottom:.21999rem;padding-top:.78001rem}h2{font-size:1.953125rem;margin-bottom:.1835837rem;padding-top:.8164163rem}h3{font-size:1.6457143rem;margin-bottom:.07599rem;padding-top:.92401rem}h4{font-size:1.5625rem;margin-bottom:.546865rem;padding-top:.453135rem}h5{font-size:1.25rem;margin-bottom:-.56251rem;padding-top:.56251rem}h6{font-size:1rem;margin-bottom:-.65001rem;padding-top:.65001rem}.splashscreen-dillinger{width:800px}.splashscreen p{font-size:1.25rem;margin-bottom:1.43749rem;padding-top:.56251rem}.title{font-size:.8rem;margin-bottom:.77999rem;padding-top:.22001rem}.title-document{margin-bottom:.89999rem;padding-top:.10001rem}.title-document,.settings a{font-size:1.25rem}.words{font-size:.8rem;margin-bottom:.77999rem;padding-top:.22001rem}.modal--dillinger.about .modal-dialog,#zen{font-size:1.25rem}#editor{font-size:1rem}}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}.form-horizontal .control-label{text-align:right;margin-bottom:0;padding-top:7px}.form-horizontal .form-group-lg .control-label{padding-top:14.3px}.form-horizontal .form-group-sm .control-label{padding-top:6px}.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}@media screen and (max-width:1200px){#_default_{max-width:30%}#_default_ ._default_{font-size:.825rem;line-height:.875rem;padding:12px 12px 6px 24px;text-align:justify}}@media screen and (max-width:1100px){#_default_{max-width:27%}#_default_ ._default_{font-size:.8rem;line-height:.85rem;padding:12px 6px 6px 24px;text-align:justify}}@media screen and (max-width:1000px){#_default_{max-width:24%}#_default_ ._default_{font-size:.775rem;line-height:.8rem;padding:12px 6px 6px 24px;text-align:justify}}@media screen and (max-width:900px){#_default_{max-width:30%}}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;overflow-x:auto;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd;-webkit-overflow-scrolling:touch}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}@media screen and (max-width:720px){#_default_{max-width:60%}#_default_ ._default_{font-size:.75rem;line-height:1rem;padding:12px 24px}}@media screen and (max-width:620px){#_default_{max-width:50%}#_default_ ._default_{font-size:.66rem;letter-spacing:1px;line-height:1rem;padding:10px 24px}}@media screen and (max-width:520px){#_default_ ._default_{font-size:.4rem;line-height:.875rem;padding:6px 12px 6px 24px;text-align:justify}}@media screen and (max-width:460px){#_default_{display:none}}@media screen and (max-width:46.1875em){.editor-header{display:none}.editor-header--first{display:block;width:100%}}</style></head><body id="preview"><p>This time, it’s not the usual blog entry. I want to point out that my blog post about Gatling load testing has been published on my employer’s blog. If you want to read it you can find it <a href="https://blog.codecentric.de/en/">here</a>. Feel free to make comments!</p>Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com0tag:blogger.com,1999:blog-2497944031743543766.post-41770190989133757942016-06-22T10:33:00.000+02:002016-06-22T10:33:27.697+02:00camunda BPM platform OSGi 2.0.0 released<p>It has been a while since we had the last release of camunda BPM platform OSGi that included some new features and I am glad to be able to announce the new major version today.</p><p>The new version includes one new feature,some dependency adjustments and a restructuring of the whole project.</p><p>The new feature is the OSGi Event Bridge, which I already explained <a href="http://wrongtracks.blogspot.kr/2016/02/camunda-bpm-osgi-event-bridge.html">here</a>. So now you'll be able to receive camunda process events in an OSGi way.</p><p>The most notable change in the dependencies is the change from OSGi 4.2 to version 4.3. This version enables e.g. the usage of generics and of the <code>Require-Capability</code>and <code>Provide-Capability</code> headers (one example how you could use them is explained in <a href="http://wrongtracks.blogspot.kr/2016/02/what-can-capabilities-do-for-your.html">another blog post</a>).</p><p>Finally, the whole project is now more modularized. Using one of the 1.x.x versions, many features were included in the camunda-bpm-osgi module, which you always needed. That ways, you would always have the classes for file install, process application or Blueprint present, if you used them or not. With the new structure you can better choose, which features you want to use and which not to.</p><p>Configadmin, Fileinstall and Processapplication are now separate bundles and no longer contained in camunda-bpm-osgi. What is left in the "main" module are the capabilities to find process definitions in your bundles, EL resolving, locating scripting engines and utility classes, e.g. for classloading. Also, all integration tests (except for the Karaf ones), are now located in a central itest module.</p><p>I hope all those changes ease the use for you to combine the powers of OSGi and camunda BPM. If you have any feedback or would like to make a wish for a new feature, feel free to leave a comment, open an issue on <a href="https://github.com/camunda/camunda-bpm-platform-osgi/tree/master/camunda-bpm-osgi/src/main/java/org/camunda/bpm/extension/osgi">GitHub</a> or open a pull request.</p>Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com11tag:blogger.com,1999:blog-2497944031743543766.post-84842842572053176932016-02-24T14:12:00.000+01:002016-02-27T10:44:50.742+01:00Extension/Service/Plugin mechanisms in Java<div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-DHM0fGJyBQE/VsvmpvTSgJI/AAAAAAAABj8/ygm8gkptdMY/s1600/noun_341771_cc.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="200" src="https://1.bp.blogspot.com/-DHM0fGJyBQE/VsvmpvTSgJI/AAAAAAAABj8/ygm8gkptdMY/s200/noun_341771_cc.png" width="200" /></a></div>Since I started to deep dive into OSGi I was wondering more and more how frameworks that have some way of extension mechanism, e.g. <a href="http://camel.apache.org/">Apache Camel</a> where you can define your own endpoint or the <a href="https://www.blogger.com/www.eclipse.org">Eclipse IDE</a> with its plugins, handle finding and instantiating extensions. I remember very well a presentation from the JAX 2013, it was by <a href="https://twitter.com/kaitoedter">Kai Tödter</a>, where he showed the combination of Vaadin and OSGi. While the web app was running he could add and remove menu entries, just by starting and stopping the bundles.<br />For a while now I have taken a look at several approaches on how to create an extensible application and you can find resources for every single method. I want to give a medium sized (not short ;)) overview here of the different ways I know to make a Java application extensible. Also, I will add a list of advantages and disadvantages, from my point of view, to each method. For every method I try to give a simple example.<br />To avoid confusion, when I write about the advantages and disadvantages, I will write from the point of view, as if you want to provide this extension mechanism in your framework, not from the API consumer point of view.<br /><h2>Passing the object</h2>This is the most obvious method. The framework defines a method which takes the SPI interface and you simply pass the object. Camel, next to other methods, makes use of this (example taken from <a href="http://camel.apache.org/how-do-i-add-a-component.html">the Camel FAQ</a>):<br /><pre><code>CamelContext context = new DefaultCamelContext();<br />context.addComponent("foo", new FooComponent(context));<br /></code></pre>Internally, Camel doesn't do much magic (code taken from <a href="https://github.com/apache/camel/blob/master/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java#L369">Camel on GitHub</a>).<br /><pre><code>public void addComponent(String componentName, final Component component) {<br /> ObjectHelper.notNull(component, "component");<br /> synchronized (components) {<br /> if (components.containsKey(componentName)) {<br /> throw new IllegalArgumentException("Cannot add component as its already previously added: " + componentName);<br /> }<br /> component.setCamelContext(this);<br /> components.put(componentName, component);<br /> for (LifecycleStrategy strategy : lifecycleStrategies) {<br /> strategy.onComponentAdd(componentName, component);<br /> }<br /><br /> // keep reference to properties component up to date<br /> if (component instanceof PropertiesComponent &amp;&amp; "properties".equals(componentName)) {<br /> propertiesComponent = (PropertiesComponent) component;<br /> }<br /> }<br />}<br /></code></pre>Every component has to have an unique name and is somehow bound to a lifecycle. Removal of a component is also possible, but has to be made somewhere from the user code.<br /><h3>Advantages</h3><ul><li>Easy and straightforward</li><li>No need for an additional framework</li><li>Compiler checks for the correct interface</li></ul><h3>Disadvantages</h3><ul><li>Access to central class (the plugin/service/component holder) is necessary</li><li>Allowing changes during the runtime is possible but complicated, since it has to be assured the component is removed everywhere</li><li>Your framework has to take care of the whole component lifecycle and any additional requirements it enforces</li></ul><h2>Interface and Reflection</h2>This method is used quite often (basically it is also how the ServiceLoader works, see next section) and you can find it with small variances. The differences are where and how exactly interface and implementation name reach the application. Placing them somewhere inside a properties file or passing them to the framework during startup are most common. The implementation is then instantiated using reflection. Creating a context with an <code>InitialContextFactory</code> works like this e.g.:<br /><pre><code> Properties env = new Properties();<br /> env.put(Context.INITIAL_CONTEXT_FACTORY,<br /> "org.jboss.naming.remote.client.InitialContextFactory");<br /></code></pre><h3>Advantages</h3><ul><li>Easy and straightforward</li><li>No need for an additional framework</li><li>No need to provide central class (in properties file approach)</li></ul><h3>Disadvantages</h3><ul><li>No type safety (if text based)</li><li>Your framework has to take care of the whole lifecycle and any additional requirements it enforces</li><li>Check for correct wiring only during runtime (if text based, check either at startup or when the code is being called, where the former is better than the latter)</li></ul><h2>java.util.ServiceLoader</h2>Frameworks using the <code>java.util.ServiceLoader</code> can also be found quite often. What the ServiceLoader does is, it uses during runtime a ClassLoader and checks the META-INF/services directory for a text file, whose name equals the passed interface (SPI) name and then reads the class name inside that file. Then it instantiates the class via Reflection. All the magic happens in the <code>LazyIterator</code> inside the ServiceLoader class (see <a href="http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/ServiceLoader.java#ServiceLoader.LazyIterator">OpenJDK</a>). Basically, it's just reading a file and instantiating the object. E.g. Camel and HiveMQ use this method.<br /><h3>Advantages</h3><ul><li>Easy and straightforward</li><li>ServiceLoader is part of JDK</li><li>No need for an additional framework</li></ul><h3>Disadvantages</h3><ul><li>No lifecycle</li><li>Class has to provide standard constructor</li><li>Support for runtime changes must be implemented (as mentioned <a href="https://docs.oracle.com/javase/tutorial/ext/basics/spi.html">here</a>)</li><li>Check for correct wiring only during runtime (the filename or the string inside the file could be wrong)</li></ul><h2>(Eclipse) Extension Points</h2><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody><tr><td style="text-align: center;"><a href="https://4.bp.blogspot.com/-L2zZrps1obE/Vs2p0nx2BhI/AAAAAAAABkM/uIaUsLYpgWs/s1600/687474703a2f2f747261632e6564676577616c6c2e6f72672f7261772d6174746163686d656e742f77696b692f547261634465762f436f6d706f6e656e744172636869746563747572652f78746e70742e706e67.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="124" src="https://4.bp.blogspot.com/-L2zZrps1obE/Vs2p0nx2BhI/AAAAAAAABkM/uIaUsLYpgWs/s320/687474703a2f2f747261632e6564676577616c6c2e6f72672f7261772d6174746163686d656e742f77696b692f547261634465762f436f6d706f6e656e744172636869746563747572652f78746e70742e706e67.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Picture under BSD license, see <a href="https://github.com/progrium/go-extpoints/blob/master/LICENSE">here</a></td></tr></tbody></table>As far as I know the concept of Extension Points never got popular outside Eclipse, although it is possible to include them in every application. To achieve loose coupling the definition of places where you can add your plugin and the plugins themselves is extracted into XML files.<br />To define an extension point you need something like this:<br /><pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br />&lt;?eclipse version="3.4"?&gt;<br /> &lt;extension-point<br /> id="de.blogspot.wrongtracks.FooService"<br /> name="FooService"<br /> schema="schema/de.blogspot.wrongtracks.FooService.exsd"/&gt;<br /></code></pre>The extension provider then has to define an appropriate extension for that point:<br /><pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br />&lt;?eclipse version="3.4"?&gt;<br />&lt;plugin&gt;<br /> &lt;extension<br /> point="de.blogspot.wrongtracks.FooService"&gt;<br /> &lt;implementation<br /> class="com.example.impl.FooServiceImpl"<br /> id="com.example.impl.FooServiceImpl"<br /> name="FooServiceImpl"&gt;<br /> &lt;/implementation&gt;<br /> &lt;/extension&gt;<br />&lt;/plugin&gt;<br /></code></pre>I got to admit, that I am not completely sure how exactly you can integrate the extension points, but I guess you will need quite a lot from the basic Eclipse runtime. There is a <a href="https://angelozerr.wordpress.com/2010/09/14/eclipse-extension-points-and-extensions-without-osgi/">blog post</a>, which explains how you can use extension points without depending on OSGi.<br /><h3>Advantages</h3><ul><li>Extensions can be added during runtime</li><li>Good tool support inside Eclipse</li><li>Wrong wiring only affects single extension</li><li>Loose coupling (more or less, since the extensions depend on the extension point id)</li></ul><h3>Disadvantages</h3><ul><li>Dependencies to Eclipse</li><li>Overhead from the Eclipse platform (I actually cannot prove this point but I assume there must be a considerate overhead involved in comparison to the previous methods)</li><li>Check for correct wiring only during runtime</li></ul><h2>Spring XML<div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-lTQ8bziCBiI/Vs2vSt8mfXI/AAAAAAAABkk/98VaQvrsMlM/s1600/leaf-299931_640.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="137" src="https://1.bp.blogspot.com/-lTQ8bziCBiI/Vs2vSt8mfXI/AAAAAAAABkk/98VaQvrsMlM/s200/leaf-299931_640.jpg" width="200" /></a></div></h2>The Spring framework tried to find a way for loosely coupled components long before CDI, as we know it today, appeared. Their solution was an XML file in which the different classes are being wired together (I am well aware of the fact that nowadays there are also <a href="http://docs.spring.io/autorepo/docs/spring/4.2.x/spring-framework-reference/html/beans.html#beans-factory-metadata">other ways</a>, but since they are also based on annotations they don't differ enough from CDI as that I'll give them an own paragraph). In the basic XML file you define all your beans and Spring will take care of the instantiation. It is also possible to distribute the configuration among several XML files. A very simple example (taken and modified from <a href="http://docs.spring.io/autorepo/docs/spring/4.2.x/spring-framework-reference/html/beans.html#beans-factory-instantiation">the Spring documentation</a>) looks like this:<br /><pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br />&lt;beans xmlns="http://www.springframework.org/schema/beans"<br /> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br /> xsi:schemaLocation="http://www.springframework.org/schema/beans<br /> http://www.springframework.org/schema/beans/spring-beans.xsd"&gt;<br /> &lt;bean id="accountDao"<br /> class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao"&gt;<br /> &lt;/bean&gt;<br /><br /> &lt;bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl"&gt;<br /> &lt;property name="accountDao" ref="accountDao"/&gt;<br /> &lt;/bean&gt;<br />&lt;/beans&gt;<br /></code></pre>If you want to provide your users a way to add their services/plugins to the framework, you'll have to provide a setter method where the users can add their object. E.g. like this (taken from <a href="https://docs.camunda.org/manual/7.3/guides/user-guide/#spring-framework-integration-process-engine-configuration-configuring-a-process-engine-plugin-in-spring">camunda documentation</a>):<br /><pre><code>&lt;bean id="processEngineConfiguration" class="org.camunda.bpm.engine.spring.SpringProcessEngineConfiguration"&gt;<br /> ...<br /> &lt;property name="processEnginePlugins"&gt;<br /> &lt;list&gt;<br /> &lt;bean id="spinPlugin" class="org.camunda.spin.plugin.impl.SpinProcessEnginePlugin" /&gt;<br /> &lt;/list&gt;<br /> &lt;/property&gt;<br />&lt;/bean&gt;<br /></code></pre><h3>Advantages</h3><ul><li>Spring is lightweigth</li><li>Lifecycle support from Spring</li></ul><h3>Disadvantages</h3><ul><li>XML needs to be maintained</li><li>No auto detection, users have to write the XML when they want to add something</li><li>The Spring IoC container is needed</li><li>Correct wiring is only checked at startup</li></ul><h2>OSGi Services</h2>OSGi was created embracing runtime changes and bundles dynamically providing and removing their services. With this in mind OSGi strongly supports applications being extended by services, provided by different bundles. The simplest approach is to implement a <code>ServiceListener</code> or a <code>ServiceTracker</code>. Both should be created on bundle start and they will react when a new implementation of the service appears. A <code>ServiceListener</code> can be as simple as this (taken from <a href="https://www.blogger.com/www.knopflerfish.org/osgi_service_tutorial.html">the Knoplerfish tutorial</a>):<br /><pre><code> ServiceListener sl = new ServiceListener() {<br /> public void serviceChanged(ServiceEvent ev) {<br /> ServiceReference sr = ev.getServiceReference();<br /> switch(ev.getType()) {<br /> case ServiceEvent.REGISTERED:<br /> {<br /> HttpService http = (HttpService)bc.getService(sr);<br /> http.registerServlet(...);<br /> }<br /> break;<br /> default:<br /> break;<br /> }<br /> }<br /> };<br /><br /> String filter = "(objectclass=" + HttpService.class.getName() + ")";<br /> bc.addServiceListener(sl, filter);<br /></code></pre>Where bc is a <code>BundleContext</code> object. And a <code>ServiceTracker</code> can be used like this:<br /><pre><code>ServiceTracker&lt;HttpService,HttpService&gt; serviceTracker = new ServiceTracker&lt;HttpService, HttpService&gt;(bc, HttpService.class, null);<br />serviceTracker.open();<br /></code></pre>There are more elegant ways to get hold of an OSGi service using <a href="http://wiki.osgi.org/wiki/Blueprint">Blueprint</a>, <a href="http://wiki.osgi.org/wiki/Declarative_Services">Declarative Services</a> or the <a href="http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager.html">Apache Felix Dependency Manager</a> but the <code>ServiceListener</code> is the basic way.<br /><h3>Advantages</h3><ul><li>OSGi lifecycle support</li><li>Changes during runtime "encouraged" ;)</li><li>Compiler checks wiring (not for the <code>ServiceListener</code> but for the rest)</li><li>Problems with services are restricted to single bundle</li></ul><h3>Disadvantages</h3><ul><li>You have to buy the whole OSGi package: imports, exports, bundles and everything</li><li>Having the full OSGi lifecycle makes the world more complicated since every service can disappear at every moment</li></ul><h4>Note about PojoSR/OSGi Light</h4><p>Since the biggest disadvantage of OSGi is that you have to get the whole package, I want to mention here another approach, which is called PojoSR or OSGi Light. The goal of it is to give you the OSGi service concept without the rest that comes with OSGi. Unfortunately, I could not find much documentation about it and the activity around this project seems to be very low at the moment. There is an article <a href="http://wiki.osgi.org/wiki/PojoSR">here</a> and the PojoSR framework <a href="https://code.google.com/archive/p/pojosr/wikis/Usage.wiki">itself</a>. Also, it looks like PojoSR is now a part of Apache Felix called <a href="https://github.com/apache/felix/tree/trunk/connect">"Connect"</a>, but its version is 0.1.0. So if anyone of you knows more about it, please let me know.</p><h2>CDI</h2><div class="separator" style="clear: both; text-align: center;"><a href="https://2.bp.blogspot.com/-WGsPq53SzAU/Vs2rM8Jp20I/AAAAAAAABkY/uaQIFJkiGHo/s1600/noun_183548_cc.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="200" src="https://2.bp.blogspot.com/-WGsPq53SzAU/Vs2rM8Jp20I/AAAAAAAABkY/uaQIFJkiGHo/s200/noun_183548_cc.png" width="200" /></a></div>Contexts and Dependency injection was a big step for Java EE, allowing developers to write more loosely coupled code. The CDI container takes care of automagically wiring the different parts together. The developer only has to use the correct annotations. Depending on which CDI beans are present at runtime, concrete implementations can be changed without changing the code that uses them. When trying to use a class the basic injection looks like this:<br /><pre><code>@Inject<br />private MyServiceInterface service;<br /></code></pre>If there is need to get all of the implementations (which we actually want here), then the class <code>Instance</code> must be used:<br /><pre><code>@Inject @Any<br />private Instance&lt;MyServiceInterface&gt; services;<br /></code></pre>Since <code>Instance</code> is an <code>Iterable</code> a simple for-each loop can be used to access all the objects. Alternatively the <code>select()</code> method can be used to further specify requirements.<br /><h3>Advantages</h3><ul><li>Compiler checks for correct type</li><li>CDI container checks correct wiring at startup</li><li>Part of JEE standard but can also be used without application serve (use a JSR-330 implementation like Guice or HK2)r</li><li>CDI lifecycle support</li></ul><h3>Disadvantages</h3><ul><li>A CDI container is needed</li><li>Changes during runtime are not possible</li><li><a href="http://www.annotatiomania.com/">Annotatiomania</a> (at least if you don't watch out)</li></ul><h2>Summary</h2>As you can see many different frameworks/methods evolved in the Java ecosystem. Every single one with its specific advantages and disadvantages. I think we can summarize the different extension mechanisms as three types (with their members):<br /><ol><li>String and well-known location ("Interface and Reflection", "ServiceLoader", "(Eclipse) Extension Points", "Spring XML")</li><li>Programmatic wiring ("Passing the object", "Interface and Reflection", "OSGi Services")</li><li>Classpath scanning ("CDI")</li></ol>Of course the three types are not exclusive. You may provide your users more than one way and let them choose. Also CDI is not exactly the only framework that uses classpath scanning. Spring with its two other ways for configuring the IoC container relies on that method, too.<br /><br/>I hope this article provides an good and sufficient overview of the different methods on how to create an extensible framework. Choosing the right one will make your users surely happy. If you know another method, which I forgot, please let me know, I will gladly add it here.<br /><br/>Please note that the lists of advantages and disadvantages are based on my reasoning. I tried to be objective but like every programmer I have my favorites and my experiences with the frameworks that may make me a little bit biased.Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com0tag:blogger.com,1999:blog-2497944031743543766.post-11824590867905263632016-02-20T11:12:00.000+01:002016-02-20T11:13:51.832+01:00What can capabilities do for your processes?Before we release camunda BPM OSGi 2.0 I want to do a little bit more of advertisement for it and show what is possible with the new version. One change in the new version will be, that it depends on OSGi 4.3 and no longer 4.2. One change, besides the fact that I can now use generics in the code (yay!) is that with OSGi 4.3 the capabilities headers will work. So, what's so impressive about them?<br /><h2>Capability headers</h2>The capability headers are two header <code>Provide-Capability</code> and <code>Require-Capability</code>. They are a further abstraction of the <code>Import-Package</code> and <code>Export-Package</code> headers we all (should ;)) know. But with the capability headers you are not as limited as with the package headers. Arbitrary things can be defined, e.g.<br /><pre><code>Provide-Capability: sensor; type=gyro<br /></code></pre>would be a valid statement. But you are not limited to one attribute:<br /><pre><code>Provide-Capability: sensor; type=heat; minTemp=0; maxTemp=100<br /></code></pre>is also possible. And the bundle that requires such capabilities can use an LDAP filter expression:<br /><pre><code>Require-Capability: sensor; filter:="(&amp;(type=type=heat)(minTemp=0)(maxTemp=100))"<br /></code></pre>That ways it is possible to find exactly what is needed in a way that allows to specify more than just packages and versions.<br />How can you use this for your business processes?<br /><h2>Capability headers for processes</h2>One use-case that came quickly to my mind were process definitions that depend on each other, e.g. if you have a process with a call activity. An example could look like this (please excuse that I didn't prepare an exhaustive example):<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://4.bp.blogspot.com/-SXOEuII0eVA/VsBnLA4iNvI/AAAAAAAABjE/i8fgK5zu5ZM/s1600/diagram.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://4.bp.blogspot.com/-SXOEuII0eVA/VsBnLA4iNvI/AAAAAAAABjE/i8fgK5zu5ZM/s1600/diagram.png" /></a></div>Let's call this one the "Hunger process". And the callee process, the "Phone process" can be as simple as this:<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-_-PICo3bei8/VsBnmntjenI/AAAAAAAABjM/1HkT2MYPuvo/s1600/diagram%25281%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://1.bp.blogspot.com/-_-PICo3bei8/VsBnmntjenI/AAAAAAAABjM/1HkT2MYPuvo/s1600/diagram%25281%2529.png" /></a></div><br /><br />The last time I checked there is nothing that would stop you to try to start the Hunger process although the Phone process hasn't been deployed yet. If the Hunger process would be something that you want to start automatically you would run into a nasty exception. Here, the headers can help. You could simply describe in your MANIFEST that you require the Phone process before your bundle can be started:<br /><pre><code>Require-Capability: process; filter:="(key=Phone_process)"<br /></code></pre>You could also add a version number or whatever seems useful. The bundle containing the Phone process should then of course contain the appropriate part:<br /><pre><code>Provide-Capability: process; key=Phone_process<br /></code></pre>So, when you deploy the bundle with the Hunger process it cannot be started without the bundle containing the Phone process. That ways you can manage your process interdependencies without running into exceptions.<br />Finally, if you use the maven-bundle-plugin I want to give you a short example.<br /><h2>Setting the headers with the maven-bundle-plugin</h2>With the maven-bundle-plugin it is really easy to set the headers. I'll suppose that you use <code>&lt;packaging&gt;bundle&lt;/packaging&gt;</code> in your POM. Here's how you can set the headers:<br /><pre><code>&lt;plugin&gt;<br /> &lt;groupId&gt;org.apache.felix&lt;/groupId&gt;<br /> &lt;artifactId&gt;maven-bundle-plugin&lt;/artifactId&gt;<br /> &lt;extensions&gt;true&lt;/extensions&gt;<br /> &lt;configuration&gt;<br /> &lt;instructions&gt;<br /> &lt;Provide-Capability&gt;process; key=Phone_process&lt;/Provide-Capability&gt;<br /> &lt;/instructions&gt;<br /> &lt;/configuration&gt;<br /></code></pre>See, piece of cake ;)<br /> <br/>I hope I could give you some idea how you could use the capability headers that OSGi 4.3 introduced. This was just a quick example but I think it shows nicely, how OSGi can support your BPMN processes.Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com0tag:blogger.com,1999:blog-2497944031743543766.post-15205602268774929042016-02-13T11:05:00.000+01:002016-02-13T11:05:10.574+01:00camunda BPM OSGi - Event BridgeI have implemented the eventing feature already some months ago but I haven't managed to advertise it a little bit more until now. So, let's praise my work ;)<br /><br />I'll start with some background information, which you can skip if you're familiar with camunda BPM and the OSGi EventAdmin. Then, some information about the what and how follows.<br /><br />Let's start with OSGi eventing.<br /><h2 id="osgi-event-admin">OSGi Event Admin</h2>The Event Admin is a part of the OSGi Compendium Specification. It is a way to communicate between bundles in a decoupled way by sending events. The communication follows a publish/subcribe scheme.<br /><br />One bundle obtains the <code>EventAdmin</code> service, creates an <code>Event</code> object and sends it. Every event is created with a certain topic and can contain arbitrary String properties in a key-value way. Topics are hierarchical separated by a "/" and wildcards are allowed. E.g. <code>org/osgi/framework/BundleEvent/STARTED</code> is a topic used by the OSGi framework.<br /><br />Events can be sent in a synchronous or asynchronous way and additional LDAP filters can be used based on the properties.<br /><br />You can find a good example on the <a href="http://felix.apache.org/documentation/subprojects/apache-felix-event-admin.html">Apache Felix website</a>.<br /><br />Now that we know a little bit about the EventAdmin let's take a look at camunda BPM.<br /><h2 id="camunda-bpm-events">camunda BPM events</h2>During the execution of a process certain events occur, e.g. a task is being assigned or a process end. To be able to "see" those events the user has to register either an <code>ExecutionListener</code> or a <code>TaskListener</code> (for more details see <a href="https://docs.camunda.org/manual/7.4/user-guide/process-engine/delegation-code/#execution-listener">here</a> and <a href="https://docs.camunda.org/manual/7.4/user-guide/process-engine/delegation-code/#task-listener">here</a>).<br /><br />The common way to register the listeners is to directly add them to the process definition, i.e. the .bpmn file. But there are certainly cases where we do not own the process file but would like to receive events (e.g. for monitoring).<br /><br />Let's see how to achieve this in an OSGi environment.<br /><h2 id="camunda-bpm-osgi-event-bridge">camunda BPM OSGi - Event Bridge</h2>I gotta admit the idea of an event bridge is not my own, because the CDI extension for camunda BPM already has an <a href="https://docs.camunda.org/manual/7.4/user-guide/cdi-java-ee-integration/the-cdi-event-bridge/">CDI event bridge</a>. Anyways, for OSGi this feature was missing. I'll explain to you what happens internally and how you can use it.<br /><h3 id="what-happens-">What happens?</h3>The OSGi event bridge implementation exports a service that is a <code>BpmnParseListener</code>. Whenever the engine parses a process definition this listener will become active and attach <code>TaskListener</code> and <code>ExecutionListener</code> wherever possible. But these listeners aren't full implementations. They are dynamic proxies with a special <code>InvocationHandler</code>.<br /><br />When the <code>InvocationHandler</code> is being invoked it checks if the OSGi event bridge is still active and if the <code>EventAdmin</code> is present. If yes, it instantiates a new <code>OSGiEventDistributor</code>, which creates a new event and fills the properties.<br /><br />I've tried to use all properties the camunda events provide and put them into the event properties. You can see a full list in <a href="https://github.com/camunda/camunda-bpm-platform-osgi/blob/master/camunda-bpm-osgi-eventing-api/src/main/java/org/camunda/bpm/extension/osgi/eventing/api/BusinessProcessEventProperties.java">this class</a>.<br /><br />This is basically what is happening. So, what can you do with the event bridge?<br /><h3 id="how-to-use-it-">How to use it?</h3>Before you can make use of the OSGi event bridge you have to add the <code>OSGiEventBridgeActivator</code> as a <code>BpmnParseListener</code> to your <code>ProcessEngineConfiguration</code>. You do this with the method <code>setCustomPreBPMNParseListeners()</code>. Unfortunately, there is no way to add the listener to an already created engine. After adding the listener events are being published. The event topics are:<br /><ul><li><code>org/camunda/bpm/extension/osgi/eventing/TaskEvent</code></li><li><code>org/camunda/bpm/extension/osgi/eventing/Execution</code></li></ul>Of course you can use an asterisk after <code>../eventing/</code> to match both.<br /><br />Wherever you want to listen to events, you can create your own <code>EventHandler</code> and subscribe to the topic you need/want. A simple example would be:<br /><pre><br /><code><br />EventHandler eventHandler = new EventHandler() {<br /> @Override<br /> public void handleEvent(Event event) {<br /> Logger.getLogger("Event occured: " + event.getTopic());<br /> }<br />};<br />Dictionary<String, String> props = new Hashtable<String, String>();<br />props.put(org.osgi.service.event.EventConstants.EVENT_TOPIC, org.camunda.bpm.extension.osgi.eventing.api.Topics.ALL_EVENTING_EVENTS_TOPIC);<br />bundleContext.registerService(EventHandler.class.getName(), eventHandler, props);<br /></code><br /></pre>Since many information is inside the event properties you can also use a more sophisticated LDAP filter expression based on that information. E.g. if you only want to receive events for a certain process you can do this:<br /><pre><code><br />EventHandler&nbsp;eventHandler&nbsp;=&nbsp;new&nbsp;EventHandler()&nbsp;{<br />...<br />};<br />Dictionary&lt;String,&nbsp;String&gt;&nbsp;props&nbsp;=&nbsp;new&nbsp;Hashtable&lt;String,&nbsp;String&gt;();props.put(EventConstants.EVENT_TOPIC,&nbsp;Topics.ALL_EVENTING_EVENTS_TOPIC);<br />props.put(EventConstants.EVENT_FILTER,&nbsp;"(processDefinitionId=invoice");<br />bundleContext.registerService(EventHandler.class.getName(),&nbsp;eventHandler,&nbsp;props);<br /></code><br /></pre>And that's it. At the moment there is no way to limit the applications that are allowed to receive events, so everybody can see all the events if he subscribes to them. If you have an idea how to do this in a nice way, please let me know.<br /><br />I hope you can make good use of the OSGi event bridge. My plan is to release camunda BPM OSGi 2.0.0 (which includes the event bridge) shortly after camunda BPM 7.5.0 is being released. Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com0tag:blogger.com,1999:blog-2497944031743543766.post-43556261735603898252015-06-11T07:51:00.000+02:002016-02-26T01:03:54.978+01:00camunda BPM Platform OSGi 1.2.0 releasedToday (actually secretly last week ;) ) we released camunda BPM platform OSGi 1.2.0. The release only contains a version adjustment to camunda BPM platform 7.3. So if you want to upgrade your version of camunda BPM platform, you can now enjoy the OSGi extension without worries. As always, if you have some remarks or find some bugs, please let me know.Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com5tag:blogger.com,1999:blog-2497944031743543766.post-9185056751058577262015-05-17T13:55:00.003+02:002015-05-17T13:56:57.404+02:00Back to universitySince some information is always better than none I wanted to tell my fellow readers that my blog updates will become more seldom since I went back to university. It is not that I don't want to blog anymore or that there are no topics to write about but I just can't seem to find the time to write something (due to homework, which sounds funny in my age ;)). I hope you can understand this and will return to this site in the future.Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com0tag:blogger.com,1999:blog-2497944031743543766.post-44073211226007594442015-03-05T17:19:00.000+01:002015-03-05T17:19:53.554+01:00camunda BPM Platform OSGi 1.1.0 releasedIt has been more than half a year since the last release of camunda BPM Platform OSGi (version 1.0.0). The last version was something special because it was the first major release of the new OSGi module for the camunda BPM Platform. Today we release the first minor version 1.1.0 (you can find it <a href="https://github.com/camunda/camunda-bpm-platform-osgi/releases/tag/1.1.0">here</a> or on Maven Central). So, what's new?<br /><ol><li>Configuration Admin Service integration with ManagedProcessEngineFactory</li><li>Karaf commands now work for version 2 and 3</li><li>Karaf assembly module</li><li>Platform API integration (OSGiProcessApplication)</li></ol><h2>Configuration Admin Service Integration</h2>The camunda BPM OSGi module now registers a ManagedServiceFactory when the OSGi compendium classes are present. You can find a description in the <a href="https://github.com/camunda/camunda-bpm-platform-osgi#using-the-configurationadmin-service">README</a> how to use it. I have to say that I really like the idea behind the Configuration Admin.<br /><h2>Karaf commands now work for version 2 and 3</h2>Until today there was a branch open for the Karaf commands under version 3. Since the API our commands implement didn't change from version 2 to 3 the range was "widened" so the imports now cover both versions.<br /><h2>Karaf assembly module</h2>The project now contains a new Maven module. It's called "camunda-bpm-karaf-assembly". The module makes use of the Karaf Maven plug-in and let's you assemble a custom Apache Karaf including the camunda bundles. If you need a Karaf for test purposes you'll be able to quickly assemble a compressed Karaf and use it. Also, the <a href="http://camunda.org/team/">camunda team</a> will add the custom Karaf to the download section next to the other distributions.<br /><h2>Platform API integration</h2>This took a lot of effort and the combined knowledge of <a href="https://github.com/meyerdan">Daniel</a>, <a href="https://github.com/romansmirnov">Roman</a> and me. The changes were lurking for a while inside a branch until they got finally merged into camunda BPM OSGi and the camunda BPM Platform. Again, the <a href="https://github.com/camunda/camunda-bpm-platform-osgi#using-the-processapplication-api">README</a> is a good starting point ;-)<br />For me, personally, the biggest reason to release 1.1.0 is to start working on version 2.0.0. As you can guess from the new major version I have planned some bigger changes. My target is to split the different parts, which are contained in camunda-bpm-osgi into separate modules. I'll post my thoughts soon in the <a href="https://groups.google.com/forum/#!forum/camunda-bpm-dev">developer forum</a>. Feel free to join the discussion.<br />And, of course, have fun with the new version and its new features.Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com1tag:blogger.com,1999:blog-2497944031743543766.post-81449991678681997752015-01-29T08:40:00.000+01:002015-01-29T08:40:53.269+01:00Cluster your service with the ConfigurationAdmin and Apache Karaf Cellar using the camunda BPM engine as example<h2>Introduction</h2><p>Initially, this was supposed to be a short introduction about the topic in the title and an opportunity for me to get to know Apache Karaf Cellar. Unfortunately, I couldn't finish the topic until today because I had some unexpected problems. So basically this is going to be a post about the problems I encountered. At the end you'll find a TL;DR; if you just want to get started.</p><h2>Short introduction into the Configuration Admin Service</h2><p>From the OSGi wiki: "Configuration Admin is a service which allows configuration information to be passed into components in order to initialise them, without having a dependency on where or how that configuration information is stored."(<a href="http://wiki.osgi.org/wiki/Configuration_Admin">http://wiki.osgi.org/wiki/Configuration_Admin</a>)</p><p>Basically you write a key-value property and a service which can use it. All the "magic" is done by the ConfigurationAdminService, which is part of the OSGi Compendium Specification. A good introduction can be found <a href="http://felix.apache.org/documentation/subprojects/apache-felix-config-admin.html">here</a>. Also the Admin will store it somewhere for you.</p><h2>Short introduction into Apache Karaf Cellar</h2><p>Taken from the Cellar website: "Cellar is a clustering solution for Apache Karaf powered by Hazelcast. Cellar allows you to manage a cluster of Karaf instances, providing synchronisation between instances."(<a href="http://karaf.apache.org/index/subprojects/cellar.html">http://karaf.apache.org/index/subprojects/cellar.html</a>)</p><p>I liked the idea to provide a service on one Karaf instance and see it appear on every instance in the cluster. Especially the combination with a <a href="http://www.osgi.org/javadoc/r4v42/org/osgi/service/cm/ManagedServiceFactory.html">MangedServiceFactory</a> seems like a great idea.</p><p>To read more about Cellar see <a href="http://karaf.apache.org/index/subprojects/cellar.html">here</a>.</p><h2>Set up your Apache Karaf</h2><p>For my example I want to use my MangedProcessEngineFactory from the 1.1.0-SNAPSHOT version of camunda BPM OSGi. You can just clone the repository on <a href="https://github.com/camunda/camunda-bpm-platform-osgi">GitHub</a> and built it with <code>mvn install</code>.</p><p>Because I am quite lazy I started two Karaf instances on my laptop. If you want to do that, too, you'll have to change some port numbers for the second Karaf instance. First, the ports in the <code>etc/org.apache.karaf.management.cfg</code>:</p><pre><code>rmiRegistryPort<br />rmiServerPort</code></pre><p>Second the SSH port in the <code>etc/org.apache.karaf.shell.cfg</code> (forgetting this caused me a <a href="http://karaf.922171.n3.nabble.com/Automatic-synchronisation-with-Cellar-td4037055.html">some trouble</a>). Next we gotta install Cellar on each Karaf instance. Because we want to use the current version, we'll use version 3.0.1 of Cellar. You can find the general installation guide <a href="http://karaf.apache.org/manual/cellar/latest/user-guide/deploy.html">here</a> for instructions about installation and start. Basically you just have to call from the Karaf console</p><pre><code>feature:repo-add mvn:org.apache.karaf.cellar/apache-karaf-cellar/3.0.1/xml/features<br />feature:install cellar</code></pre><p>If you somehow plan to build Cellar yourself, I'll recommend to comment out the "samples" module in the root POM. All your Karaf instances should discover each other automatically. Now we got to install and share the camunda-feature (or whichever you want to use) into the cluster.</p><h3>Install and share a feature</h3><p>To do this task we have two choices. One would be to activate the listeners in every Karaf instance and use the "basic" commands. Therefore you'll have to set the bundle listener value in the <code>org.apache.karaf.cellar.node.cfg</code> to true (we won't need the other ones in this example):</p><pre><code>bundle.listener = true<br />config.listener = false<br />feature.listener = false</code></pre><p>The other choice would be to use the <code>cluster:*</code> commands. Both will (should) produce the same result so choose whichever you prefer.</p><p>As I mentioned, if you prefer the first option (listeners), you can just install everything as usual because the cluster synchronizes every change:</p><pre><code>feature:repo-add mvn:org.camunda.bpm.extension.osgi/camunda-bpm-karaf-feature/1.1.0-SNAPSHOT/xml/features<br />feature:install camunda-bpm-karaf-feature-minimal</code></pre><p>(Please note that you'll need my example project installed locally to use it)</p><p>(Also please note that there is currently a bug in the camunda feature.xml. You'll have to change the version of camunda-connect-core to 1.0.0-alpha3 to make it work)</p><p>If you want to use the "cluster-versions" of those commands, you have to type:</p><pre><code>cluster:feature-repo-add default mvn:org.camunda.bpm.extension.osgi/camunda-bpm-karaf-feature/1.1.0-SNAPSHOT/xml/features<br />cluster:feature-install default camunda-bpm-karaf-feature-minimal</code></pre><p>Those commands work like the basic ones but you always have to provide a group.</p><p>You should see that the feature got installed on both Karaf instances (check e.g. with features:list | grep -i camunda). Now we need a database.</p><h2>Setting up the database</h2><p>I gotta admit, this is were my first problems occurred. Starting from funny and ending at a being a little bit annoyed. My first problem was that I tried to use the in-memory version of H2. This won't work because, logically, every Karaf instance runs in its own JVM. So, because of multiple applications, I started h2 in server mode (see <a href="http://h2database.com/html/tutorial.html#using_server">here</a> for more information).</p><p><code>java -cp h2*.jar org.h2.tools.Server jdbc:h2:tcp://localhost/~/test</code></p><p>The next problem was that because of some exceptions the ProcessEngines started and stopped in seemingly random orders. Having the <code>databaseSchemaUpdate</code> property set to create-drop caused problems with tables not being present because of random dropping/creating. I recommend to create the tables yourself (<a href="https://github.com/camunda/camunda-bpm-platform/blob/master/engine/src/main/resources/org/camunda/bpm/engine/db/create/activiti.h2.create.identity.sql">here</a> are the sqls).</p><p>This didn't solve all of my database problems. I suspected H2 of not being capable of handling the same user logging in twice (which it is capable of as far as I know now). After that I switched to MySQL.</p><h3>Setting up MySQL in Karaf</h3><p>MySQL is a little bit more complicated to set up than H2 because we have to create a proper datasource. First, we need to install <a href="http://karaf.apache.org/manual/latest/users-guide/jdbc.html">Apache Karaf DataSources</a>:</p><p><code>feature:install jdbc</code></p><p>Next, create the datasource</p><p><code>jdbc:create -u sa -p sa -url jdbc:mysql://localhost:3306/test -t MySQL test</code></p><p>The datasource create command has to be executed on both Karafs because the datasource-*.xml that'll be created in the deploy directory won't be copied. For the ProcessEngine to be able to find the MySQL datasource it needs a JNDI name. To give a datasource a JNDI name we need <a href="http://karaf.apache.org/manual/latest/users-guide/jndi.html">Apache Karaf Naming</a>.</p><p><code>feature:install jndi</code></p><p>Now the datasource will automatically get a JNDI name (check with <code>jndi:names</code>). If you don't see the <code>jndi:*</code> commands you'll have to install the feature manually on the second Karaf.</p><p>Finally we need the MySQL connector jar. We can find it <a href="http://dev.mysql.com/downloads/connector/j/">here</a>. Simply drop the jar into the deploy directory.</p><p>The MySQL database works fine for me so far. Let's take a look at the configuration file.</p><h2>The configuration file</h2><p>When I started with this "experiment" I thought that making the use of the etc/ directory in Karaf would be a good idea but now I gotta say: Please, don't try to do this file based. I tried a lot of combinations and it didn't work out. The closest I got was the configuration arriving on both Karafs but only one engine being created. <a href="https://twitter.com/jbonofre">Jean-Baptiste</a> and <a href="https://twitter.com/anierbeck">Achim</a> were really trying to help me on the mailing list. Nevertheless, I couldn't get it running. You are free to try.</p><p>Karaf watches the etc/ directory for configuration files. To deploy one for the ManagedProcessEngineFactroy you'll have to name it <code>org.camunda.bpm.extension.osgi.configadmin.ManagedProcessEngineFactory-1.cfg</code>.</p><p>I switched to a bundle which contains the configuration.</p><h2>The configuration bundle</h2><p>As mentioned before, for a ManagedServiceFactory to create a service it needs one or more configurations. We'll use a simple version of the configuration:</p><pre><code>databaseSchemaUpdate=false<br />jobExecutorActivate=true<br />processEngineName=TestEngine<br />databaseType=mysql<br />dataSourceJndiName=osgi:service/jdbc/test</code></pre><p>If you want to try H2, the configuration would look like this:</p><pre><code>databaseSchemaUpdate=false<br />jdbcUrl=jdbc:h2:tcp://localhost/~/test<br />jobExecutorActivate=true<br />processEngineName=TestEngine<br />jdbcUsername=sa<br />jdbcPassword=sa</code></pre><p>To make it simple the bundle just uses a <code>BundleActivator</code>, gets hold of the Configuration Admin and provides the property, like this:</p><pre><code class="lang-java"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Activator</span> <span class="keyword">implements</span> <span class="title">BundleActivator</span> {</span><br /><br /> <span class="keyword">public</span> <span class="keyword">void</span> start(BundleContext context) <span class="keyword">throws</span> Exception {<br /> ServiceReference ref = context.getServiceReference(ConfigurationAdmin.class.getName());<br /> ConfigurationAdmin admin = (ConfigurationAdmin) context.getService(ref);<br /> String pid = <span class="string">"org.camunda.bpm.extension.osgi.configadmin.ManagedProcessEngineFactory"</span>;<br /> Configuration configuration = admin.createFactoryConfiguration(pid, <span class="keyword">null</span>);<br /> Hashtable properties = <span class="keyword">new</span> Hashtable();<br /> properties.put(<span class="string">"databaseSchemaUpdate"</span>,<span class="string">"false"</span>);<br /> properties.put(<span class="string">"jobExecutorActivate"</span>,<span class="string">"true"</span>);<br /> properties.put(<span class="string">"processEngineName"</span>,<span class="string">"TestEngine"</span>);<br /> properties.put(<span class="string">"databaseType"</span>,<span class="string">"mysql"</span>);<br /> properties.put(<span class="string">"dataSourceJndiName"</span>, <span class="string">"osgi:service/jdbc/test"</span>);<br /> configuration.update(properties);<br /> }</code></pre><p>The activated bundle listener should provide the bundle to all Karafs. Just drop the bundle into the deploy directory.</p><p>You should see that the configuration got shared, too. To check just run this command: <code>config:list "(service.pid=org.camunda.bpm.extension.osgi.configadmin.ManagedProcessEngineFactory*)"</code></p><h2>TL;DR;</h2><ol><li>change port numbers in <code>etc/org.apache.karaf.management.cfg</code> and <code>etc/org.apache.karaf.shell.cfg</code> if you run two instances on one machine</li><li><code>feature:repo-add mvn:org.apache.karaf.cellar/apache-karaf-cellar/3.0.1/xml/features</code></li><li><code>feature:install cellar</code><ol><li>Decide if you want to activate the listener or use the cluster:commands for the following things</li></ol></li><li><code>git clone https://github.com/camunda/camunda-bpm-platform-osgi.git</code></li><li>mvn install the project</li><li><code>feature:repo-add mvn:org.camunda.bpm.extension.osgi/camunda-bpm-karaf-feature/1.1.0-SNAPSHOT/xml/features</code></li><li><code>feature:install camunda-bpm-karaf-feature-minimal</code></li><li>set up MySQL databse</li><li><code>feature:install jdbc</code></li><li>drop MySQL connector jar into deploy directory</li><li><code>jdbc:create -u sa -p sa -url jdbc:mysql://localhost:3306/test -t MySQL test</code></li><li><code>feature:install jndi</code></li><li>create configuration bundle and drop it into deploy directory. Configuration:<pre><code>databaseSchemaUpdate=false<br />jobExecutorActivate=true<br />processEngineName=TestEngine<br />databaseType=mysql<br />dataSourceJndiName=osgi:service/jdbc/test</code></pre></li></ol><p>And you're good to go.</p><p>So, this was my trip into the Karaf Cellar world. I hope I could prove the feasibility to you. I'll leave the practical consequences as an exercise to the reader ;-)</p>Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com0tag:blogger.com,1999:blog-2497944031743543766.post-10280049227800048262014-11-18T18:34:00.001+01:002014-11-18T18:41:05.088+01:00camunda BPM engine: use custom VariableType to resist the urge to flush<h1>Introduction</h1>I hope all of you are aware of the fact that you can provide a <code>ProcessEngine</code>with your own VariableTypes. If not, I'll give you a short introduction. Please note that my descriptions are based on camunda-engine 7.1.0. There will be some changes in versoin 7.2.0 and I am not sure if my observations will still be true.<br /><br /><h3>VariableType</h3><h3>&nbsp;</h3>VariableTypes help the ProcessEngine store your process variables in the table ACT_RU_VARIABLE. I would call them a mediator between the possible variables and the database schema. There are VariableType implementations for <br /><ul><li>Boolean</li><li>Serizable</li><li>Date</li><li>Double</li><li>Integer</li><li>JPA Entities</li><li>Long</li><li>Null</li><li>Short</li><li>String</li><li>and CustomObjects (about which I'll talk later)</li></ul><br />If you try to add an object as process variable, which doesn't belong to one of those types, you'll see this exception: <br /><pre><code><br />org.camunda.bpm.engine.ProcessEngineException: couldn't find a variable type that is able to serialize \&lt;object\&gt;<br /> at org.camunda.bpm.engine.impl.variable.DefaultVariableTypes.findVariableType(DefaultVariableTypes.java:62)<br /> at org.camunda.bpm.engine.impl.persistence.entity.VariableScopeImpl.getNewVariableType(VariableScopeImpl.java:315)<br /> at org.camunda.bpm.engine.impl.persistence.entity.VariableScopeImpl.createVariableInstance(VariableScopeImpl.java:395)<br /> at org.camunda.bpm.engine.impl.persistence.entity.VariableScopeImpl.createVariableLocal(VariableScopeImpl.java:332)<br /> at org.camunda.bpm.engine.impl.persistence.entity.VariableScopeImpl.setVariable(VariableScopeImpl.java:259)<br /> at org.camunda.bpm.engine.impl.persistence.entity.VariableScopeImpl.setVariable(VariableScopeImpl.java:242)<br /> at de.blogspot.wrongtracks.StoreDataDelegate.execute(StoreDataDelegate.java:9)<br /> at org.camunda.bpm.engine.impl.delegate.JavaDelegateInvocation.invoke(JavaDelegateInvocation.java:34)<br /> at org.camunda.bpm.engine.impl.delegate.DelegateInvocation.proceed(DelegateInvocation.java:39)<br /> at org.camunda.bpm.engine.impl.delegate.DefaultDelegateInterceptor.handleInvocation(DefaultDelegateInterceptor.java:42)<br /> at org.camunda.bpm.engine.impl.bpmn.behavior.ServiceTaskJavaDelegateActivityBehavior.execute(ServiceTaskJavaDelegateActivityBehavior.java:49)<br /></code><br /></pre><br /><h3>Provide your variable type</h3><h3>&nbsp;</h3>Every ProcessEngineConfiguration should have the methods <code>setCustomPostVariableTypes(List&lt;VariableType&gt;)</code> and <code>setCustomPreVariableTypes(List&lt;VariableType&gt;)</code> so you can add your variable types when configuring the engine.<br />But wait, why are there two methods, pre and post?<br />When searching which VariableType can handle the object you want to store as process variable the engine iterates over the list of VariableTypes and the first one, which can handle the object, wins. Maybe you want your own types to have precedence over the default types.<br /><br /><h1>Flushing</h1><h1>&nbsp;</h1>Now that you know about VariableTypes I want to present to you my use case.<br /><br /><h3>The case</h3><h3>&nbsp;</h3>Imagine a process that's supposed to run synchronously (i.e. without a <a href="http://docs.camunda.org/7.1/guides/user-guide/#process-engine-transactions-in-processes-wait-states">wait state</a>) within a JTA transaction and every task needs a result from the preceding one. Additionally, the results are JPA Entities. By default the JPAEntityVariableType would take care of the entity. <br />The implementation shows that every time <code>setValue()</code> is called the JPAEntityVariableType calls <code>flush()</code> on the EntityManager. Since the process runs synchronously within a transaction the flush results in unnecessary queries on my database during process execution.<br /><br /><h3>The solution</h3><h3>&nbsp;</h3>Here comes the CustomObjectType class. The CustomObjectType only needs a name and a class to work. The class is used to determine if it can handle a certain object. The CustomObjectType stores all objects in the cache of the <code>ValueField</code>. To get rid of the flush I instantiated a CustomObjectType with the class of my result and passed it to the configuration. Now, every time I put an entity inside the process variables the CustomObjectType places them inside the cache and no flush is called.<br /><h3>&nbsp;</h3><h3>&nbsp;The downside</h3><h3>&nbsp;</h3>Well, nothing comes without a price: If I should ever need a wait state my solution won't work and I'll have to find another solution or live with the flush.<br /><h3>&nbsp;</h3><h3>Alternatives</h3><h3>&nbsp;</h3>I am not sure if my solution is the best way to solve my problem. If anyone knows a better way please let me know. <br /><h3>&nbsp;</h3><h3>Small example</h3><h3>&nbsp;</h3>I also created a small example to show the use of the CustomObjectType <a href="https://github.com/rbraeunlich/StoreVariableExample">here on GitHub</a> Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com0tag:blogger.com,1999:blog-2497944031743543766.post-70010903205906366122014-11-11T16:59:00.002+01:002014-11-13T16:12:54.076+01:00camunda BPM engine: How many tasks can you execute without wait stateToday I got quite curious today about this topic and I don't know if anyone ever wondered/tried.<br />At work I introduced the camunda BPM engine a few months ago. One requirement was not to reach any wait state during the execution. That ways we want to make sure the process ends synchronously and we don't show stale data.<br /><br />As you can image the stacktraces got pretty big when an exception occurred during the end of the process (&gt;1000 lines). So I wondered how many tasks could be executed before the java stack is full (or anything else unexpected happens).<br />To try this I wrote a simple Java class:<br /><pre><code><br /><span style="font-size: small;">public class Main {<br />private static final int NUMBER_TASKS = 100;<br /><br />public static void main(String[] args) throws InterruptedException {<br /><br /> ProcessEngineConfiguration configuration = ProcessEngineConfiguration<br /> .createStandaloneProcessEngineConfiguration();<br /> configuration.setJdbcUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");<br /> configuration<br /> .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_CREATE_DROP);<br /> configuration.setJdbcUsername("sa");<br /> configuration.setJdbcPassword("");<br /> configuration.setHistory(ProcessEngineConfiguration.HISTORY_NONE);<br /><br /> ProcessEngine engine = configuration.buildProcessEngine();<br /> BpmnModelInstance bpmn = erzeugeBpmn();<br /> engine.getRepositoryService().createDeployment()<br /> .addModelInstance("manyTasks.bpmn", bpmn).deploy();<br /> RuntimeService runtimeService = engine.getRuntimeService();<br /> ProcessInstance processInstance = runtimeService<br /> .startProcessInstanceByKey("manyTasks");<br /> System.out.println("Done");<br /><br />}<br /><br />private static BpmnModelInstance erzeugeBpmn() {<br /> AbstractFlowNodeBuilder&lt;?, ?&gt; builder = Bpmn.createProcess().id("manyTasks").executable().startEvent();<br /> for(int i = 0; i &lt; NUMBER_TASKS; i++){<br /> builder = builder.serviceTask().camundaClass(EmptyDelegate.class.getName());<br /> }<br /> return builder.endEvent().done();<br />}</span><br /></code></pre><br />As you can see, nothing fancy (and I am still happy that there is a Java API for generating BPMN).<br />My computer has two Intel Core i7 with 2.9GHz and 16GB of RAM and runs Java 1.7.0_72 64bit. To run this I used the Eclipse defaults (Kepler SR2 x64):<br /><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace; font-size: small;">--launcher.XXMaxPermSize <br />256M <br />--launcher.XXMaxPermSize <br />256m <br />-Xms40m <br />-Xmx512m</span><br /><br />Ten and 100 tasks are no problem.<br />To get an overview of the size of the stacktraces I'll add a JavaDelegate, which throws an exception at the end.<br />So the method looks like this: <br /><pre><code><br /><span style="font-size: small;">private static BpmnModelInstance erzeugeBpmn() {<br /> AbstractFlowNodeBuilder&lt;?, ?&gt; builder = Bpmn.createProcess().id("manyTasks").executable().startEvent();<br /> for(int i = 0; i &lt; ANZAHL_TASKS; i++){<br /> builder = builder.serviceTask().camundaClass(EmptyDelegate.class.getName());<br /> }<br /> return builder.serviceTask().camundaClass(ExceptionDelegate.class.getName()).endEvent().done();<br /> }</span><br /></code><br /></pre>Also, I'd like to see how log output grows, so here are the numbers. The additional task is always the exception task so the numbers are 11, 101, 1001...<br /><ul><li>11 tasks: 392 lines log</li><li>101 tasks: 1025 lines log</li><li>1001 tasks: SOF</li></ul>Yay, I reached the limit ;-)<br /><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><br /></span></span><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">Exception in thread "main" java.lang.StackOverflowError</span></span><br /><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; at java.lang.ThreadLocal$ThreadLocalMap.getEntry(ThreadLocal.java:376)</span></span><br /><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; at java.lang.ThreadLocal$ThreadLocalMap.access$000(ThreadLocal.java:261)</span></span><br /><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; at java.lang.ThreadLocal.get(ThreadLocal.java:146)</span></span><br /><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; at org.camunda.bpm.engine.impl.context.Context.getStack(Context.java:95)</span></span><br /><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; at org.camunda.bpm.engine.impl.context.Context.getCommandContext(Context.java:46)</span></span><br /><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:728)</span></span><br /><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:719)</span></span><br /><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">... </span></span><br /><br />It seems like I gotta take some smaller steps:<br /><ul><li>501 tasks: SOF</li><li>401 tasks: SOF</li><li>301 tasks: SOF</li><li>201 tasks: 1025 lines log</li><li>151 tasks: 1025 lines log</li></ul>Wait, what?<br />Yes, strangely Eclipse always shows me the same amount of lines for the exception after reaching a certain threshold. And no, I didn't limit the console output in Eclipse. If anyone knows why this limit exists, please let me know.<br /><br />The task limit I reached was 268 tasks (267 "normal" ones and one exception task).<br />I am not sure about the practical implications of my "research" but as I said, I was just curious.<br />Maybe we can agree that processes of a certain size should reach a <a href="http://docs.camunda.org/latest/guides/user-guide/#process-engine-transactions-in-processes-wait-states">wait state</a> due to organizational <b>and</b> technical reasons ;-)<br /><br />EDIT: Please note that when executing a task as multi instance every loop counts as one (I learned that the hard way ;-) )Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com0tag:blogger.com,1999:blog-2497944031743543766.post-42743748565858391662014-10-09T20:24:00.001+02:002014-10-13T19:22:25.320+02:00Assemble your custom Apache Karaf with the karaf-maven-pluginI was quite happy to find out there is a Maven Plugin with which you can assembly a full Apache Karaf and include your own features/bundles.<br />From time to time I like to test my bundles in a real environment. Because of that the plugin is a great way to save the steps of unzipping a new Karaf, adding my feature and installing it.<br />So the plugin basically serves my laziness ;-)<br />But before the lazy part starts (for me and you) we have to do some work to get the plugin running.<br /><br />I will start to describe the things I figured out. Then I will show you my final configuration and at the end I will talk about the problems I encountered.<br />Of course you can take a look at the documentation (<a href="http://karaf.apache.org/manual/latest/developers-guide/karaf-maven-plugin.html">here</a> and <a href="http://karaf.apache.org/manual/latest/developers-guide/custom-distribution.html">here</a>), too.<br /><br /><h3>Karaf-assembly</h3><h3>&nbsp;</h3>To start your assembly project you just need an empty maven project with the packaging "karaf-assembly" and the plugin, of course.<br /><br />To configure the features for the plugin (so the features will end up in the Karaf) there are three options:<br /><ol><li>startupFeature</li><li>bootFeature</li><li>installedFeature</li></ol>Here is an example:<span style="font-family: &quot;Courier New&quot;,Courier,monospace;"></span><br /><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&lt;configuration&gt;<br />&nbsp; &lt;bootFeatures&gt;<br />&nbsp;&nbsp;&nbsp; &lt;feature&gt;standard&lt;/feature&gt;<br />&nbsp;&nbsp;&nbsp; &lt;feature&gt;management&lt;/feature&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;feature&gt;camunda-bpm-karaf-feature-minimal&lt;/feature&gt;&nbsp;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp; &lt;/bootFeatures&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&lt;/configuration&gt;</span><br /><br />All three types result in a different configuration. Since I don't want to copy the documentation I'll give a very brief explanation.<br /><br /><h4>startupFeatures</h4>All the bundles from your feature will appear in the startup.properties, copied to system/ and started with the Karaf.<br /><br /><h4>bootFeatures</h4>All the bundles from your feature will be copied to system/. The features you listed will appear in org.apache.karaf.features.cfg and installed when starting Karaf. The path to your feature.xml will be added to org.apache.karaf.features.cfg as feature repository.<br /><br /><h4>installFeatures</h4>All the bundles from your feature will be copied to system/. The path to your feature.xml will be added to org.apache.karaf.features.cfg as feature repository.<br /><br />You can see that every kind of *Features gets a little bit less serious than the one before. Please note that "compile" dependencies in your POM will be treated like a startupFeature.<br /><br />All the dependencies you want to include have to be ether of type "kar" or have to have the classifier "feature" and type "xml", e.g:<br /><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&lt;dependency&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp; &lt;groupId&gt;org.apache.karaf.features&lt;/groupId&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp; &lt;artifactId&gt;standard&lt;/artifactId&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp; &lt;version&gt;3.0.2-SNAPSHOT&lt;/version&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp; &lt;classifier&gt;features&lt;/classifier&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp; &lt;type&gt;xml&lt;/type&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp; &lt;scope&gt;runtime&lt;/scope&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&lt;/dependency&gt;</span><br /><br />Other dependencies will be ignored.<br /><br />That was all I could figure out about the configuration of the plugin. Now let's have a look at my project.<br /><br /><h3>My project</h3><h3>&nbsp;</h3>As mentioned before my project contains no classes or anything under src/resources. It just has the pom.xml that looks like <a href="https://drive.google.com/file/d/0B6fecPw2wyR9Wml2MWZsanh0UEE/view?usp=sharing">this</a> (Google Drive link).<br />I added a small shell script because the karaf start file wasn't executable and because I didn't want to move to target/assembly/... every time. Also I had a small problem with Java (see following heading).<br />The script looks like this:<br /><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">export JAVA_HOME=$(/usr/libexec/java_home -v 1.6)<br />chmod 777 ./target/assembly/bin/karaf<br />./target/assembly/bin/karaf start</span><br /><br />Nothing fancy ;-) So, that's already all about my project. Finally, I want to tell you about the problems I faced.<br /><br /><h3>Issues</h3><br /><h4>Plugin version</h4>I had the problem that when a feature contained nested features the nested ones wouldn't be resolved. It took me a while and some remote debugging to find the problem. After I asked in the <a href="http://mail-archives.apache.org/mod_mbox/karaf-user/201410.mbox/browser">mailing list</a> I was told that the problem existed in my version (3.0.1) and is fixed in the next one.<br />So you should definitely use the 3.0.2-SNAPSHOT version despite the fact that it's a snapshot. <a href="https://twitter.com/jbonofre">Jean-Baptiste</a> did some great improvements in that version. The logging is way better and you can have nested features.<br /><br /><h4>Ordering of dependencies</h4>After upgrading my version I could see that all of my bundles were successfully installed into the system/ directory. But after starting my Karaf they weren't deployed. The "mvn:" URL for my feature was missing in the org.apache.karaf.features.cfg "featuresRepositories" property.<br />I found out that the problem was in the order of my dependencies.<br /><br />My feature was the first dependency and then followed the Apache Karaf dependencies. Like this:<span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&lt;dependencies&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp; &lt;dependency&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;groupId&gt;org.camunda.bpm.extension.osgi&lt;/groupId&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;artifactId&gt;camunda-bpm-karaf-feature&lt;/artifactId&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;version&gt;1.1.0-SNAPSHOT&lt;/version&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;classifier&gt;features&lt;/classifier&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;type&gt;xml&lt;/type&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;scope&gt;runtime&lt;/scope&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp; &lt;/dependency&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp; &lt;dependency&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;groupId&gt;org.apache.karaf.features&lt;/groupId&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;artifactId&gt;framework&lt;/artifactId&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;version&gt;3.0.2-SNAPSHOT&lt;/version&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;type&gt;kar&lt;/type&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp; &lt;/dependency&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp; &lt;dependency&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;groupId&gt;org.apache.karaf.features&lt;/groupId&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;artifactId&gt;standard&lt;/artifactId&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;version&gt;3.0.2-SNAPSHOT&lt;/version&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;classifier&gt;features&lt;/classifier&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;type&gt;xml&lt;/type&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;scope&gt;runtime&lt;/scope&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp; &lt;/dependency&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&lt;/dependencies&gt;</span><br /><br />The problem is that the framework Kar contains all the configuration files. So when the plugin tries to update the config-file with my feature it is not present. So be careful that the framework kar is your first dependency.<br /><br /><h4>Java 8</h4><h4>&nbsp;</h4><i>Edit:</i> As Jean-Baptiste <a href="https://twitter.com/jbonofre/status/520477787153793024">told me</a> (thank you again) the Java 8 problem is only related to version 3.0.1 which I can hereby confirm. So if you have followed my advice and use 3.0.2 you can skip this part.<br /><br />Being the young and hip person I am ;-) my MacBook was already running Java 8. When I assembled and started a Karaf it would start without a problem (at least it seemed so). But hitting tab only showed this small amount of commands:<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-qUmZOBgox30/VDP-0nF5KMI/AAAAAAAABRM/_eQQv6F7l8I/s1600/karaf_assembly_1.tiff" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-qUmZOBgox30/VDP-0nF5KMI/AAAAAAAABRM/_eQQv6F7l8I/s1600/karaf_assembly_1.tiff" height="297" width="640" /></a></div><br />Every command, even help, would answer with a NullPointerException. The NPE itself looked like this:<br /><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace; font-size: x-small;">2014-10-07 16:55:55,232 | ERROR | Local user karaf | ShellUtil&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 37 - org.apache.karaf.shell.console - 3.0.1 | Exception caught while executing command<br />java.lang.NullPointerException<br />&nbsp;&nbsp;&nbsp; at org.apache.felix.gogo.runtime.Reflective.invoke(Reflective.java:61)[37:org.apache.karaf.shell.console:3.0.1]<br />&nbsp;&nbsp;&nbsp; at org.apache.felix.gogo.runtime.CommandProxy.execute(CommandProxy.java:82)[37:org.apache.karaf.shell.console:3.0.1]<br />&nbsp;&nbsp;&nbsp; at org.apache.felix.gogo.runtime.Closure.executeCmd(Closure.java:477)[37:org.apache.karaf.shell.console:3.0.1]<br />&nbsp;&nbsp;&nbsp; at org.apache.felix.gogo.runtime.Closure.executeStatement(Closure.java:403)[37:org.apache.karaf.shell.console:3.0.1]<br />&nbsp;&nbsp;&nbsp; at org.apache.felix.gogo.runtime.Pipe.run(Pipe.java:108)[37:org.apache.karaf.shell.console:3.0.1]</span><br /><br />At first I thought something was missing. But checking the logs again, looking at what happened during startup, revealed some IllegalArgumentExceptions:<br /><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace; font-size: x-small;">2014-10-07 16:53:23,402 | INFO&nbsp; | FelixStartLevel&nbsp; | ServiceRecipe&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 19 - org.apache.aries.blueprint.core - 1.4.0 | Unable to create a proxy object for the service .component-1 defined in bundle org.apache.karaf.deployer.features at version 3.0.1 with id 25. Returning the original object instead.<br />java.lang.IllegalArgumentException</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace; font-size: x-small;">at org.objectweb.asm.ClassReader </span><br /><br />I found out (thank you internet) that this is a Java 8 related problem. The command<br /><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">export JAVA_HOME=$(/usr/libexec/java_home -v 1.6)</span><br /><br />solved my problem. To always start my Karaf with Java 6 I added this line to my start script (see previous heading).<br /><br />That was all about my karaf-maven-plugin experience. I am sure there are some more hidden things I couldn't figure out. I hope my experience will be useful for someone else.<br />Have fun with your own custom Karaf!Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com0tag:blogger.com,1999:blog-2497944031743543766.post-71195834380025730352014-09-29T19:48:00.000+02:002014-09-29T19:51:25.533+02:00Create a ProcessEngine with the ConfigurationAdminServiceThere is a new feature in the camunda BPM OSGi extension and I would like to introduce it to you. So, let's start with the news.<br /><br /><h3>What's new?</h3><h3>&nbsp;</h3>The OSGi extension now exports a <a href="http://www.osgi.org/javadoc/r4v42/org/osgi/service/cm/ManagedServiceFactory.html">ManagedServiceFactory</a> to provide another way to configure and automatically share a ProcessEngine. The factory will be automatically exported when the OSGi compendium classes are present. You can then provide your configuration and the engine will be created and exported.<br /><br />If you've never heard of the ConfigurationAdminService I would like to give you a short introduction.<br /><br /><h3>What is the ConfigurationAdminService?</h3><h3>&nbsp;</h3>The ConfigurationAdminService is supposed to make the provision and change of configuration during easier. When you provide a configuration object (a dictionary) the service will find the according ManagedService or ManagedServiceFactory based on a pid (persistent id) and pass the configuration to it.<br /><br />There are (way ;-) ) better descriptions in the <a href="http://blog.osgi.org/2010/06/how-to-use-config-admin.html">OSGi Alliance blog</a> and the <a href="http://felix.apache.org/documentation/subprojects/apache-felix-config-admin.html">Apache Felix documentation</a> if you want to learn a little bit more about it. Let's see how we can use the service.<br /><br /><h3>How to use it?</h3><h3>&nbsp;</h3>As I mentioned before the configuration is just a dictionary. The keys have to corresspondent to the fields of a <a href="http://docs.camunda.org/latest/api-references/javadoc/?org/camunda/bpm/engine/ProcessEngineConfiguration.html">ProcessEngineConfiguration</a> object. Simply create a HashTable and put everything in it you need to run your engine:<br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; Hashtable&lt;String, Object&gt; props = new Hashtable&lt;String, Object&gt;();<br />&nbsp;&nbsp;&nbsp; props.put("databaseSchemaUpdate", ProcessEngineConfiguration.DB_SCHEMA_UPDATE_CREATE_DROP);<br />&nbsp;&nbsp;&nbsp; props.put("jdbcUrl", "jdbc:h2:mem:camunda;DB_CLOSE_DELAY=-1");<br />&nbsp;&nbsp;&nbsp; props.put("jobExecutorActivate", true);<br />&nbsp;&nbsp;&nbsp; props.put("processEngineName", "TestEngine");</span><br /><br />Next you gotta get the ConfigurationAdminService and call <span style="font-family: &quot;Courier New&quot;,Courier,monospace;">createFactoryConfiguration()</span> with the following PId:<br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">org.camunda.bpm.extension.osgi.configadmin.ManagedProcessEngineFactory</span> <br /><br />There is also a constant in the ManagedProcessEngineFactory interface. After that pass your dictionary to the Configuration object by calling the <span style="font-family: &quot;Courier New&quot;,Courier,monospace;">update()</span> method. And that's it. Your ProcessEngine will be created and exported.<br /><br />Now that you know how to use the service I would like to tell you what makes it special.<br /><br /><h3>Why use the ConfigurationAdminService?</h3><h3>&nbsp;</h3>I remember when I first read about the ConfigurationAdminService my thought was: "That's a really great idea!". By using the service you have several ways of providing configuration for your ProcessEngine. The easiest thing to image is that you store your configuration files in separate bundles. Every time something changes you update that bundle.<br /><br />Depending on your environment there are more ways. In Apache Karaf you could place a file named<br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">org.camunda.bpm.extension.osgi.configadmin.ManagedProcessEngineFactory.cfg</span><br />in the etc directory. Karaf would find the factory and pass the configuration to it.<br />Apache Felix and Equinox also provide ways to read and use configuration files.<br /><br />Also, the ConfigurationAdminServices helps you to provide different configurations for different environments. At least text files are to change and provide than .class files.<br /><br />Finally I want to tell you some details about the implementation.<br /><br /><h3>How is it implemented?</h3><h3>&nbsp;</h3>I gotta admit that the implementation is not that special. The factory uses Commons BeanUtils to find the setters for the properties. Because the setters of ProcessEngineConfiguration provide a fluent way I couldn't use the classes BeanUtils or PropertyUtils. That's why I "combine" the setter-name on my own and invoke the method with MethodUtils.<br /><br />Every time the configuration of a ProcessEngine changes I stop that engine, unregister it and create a new one and register that one. That is the only way to "change" the configuration of a ProcessEngine. Maybe a ProcessEngine/Configuration needs an update() method.<br /><br />I would appreciate any hints or recommendations on how to improve the factory. Since it's my first try implementing a ManagedServiceFactory.<br /><br />So, enjoy the new service!Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com0tag:blogger.com,1999:blog-2497944031743543766.post-74426333178177168782014-09-27T18:18:00.000+02:002014-09-27T18:18:18.279+02:00camunda BPM platform OSGi presents: integration with Process Application APII am happy to announce that there is a new way to configure a ProcessEngine and deploy processes.<br />You can now use the <a href="http://docs.camunda.org/latest/guides/user-guide/#process-applications">Process Application API</a>.<br />Luckily, using this API in your project is quite easy. <br />There are three things you have to do:<br /><ol><li>&nbsp;provide a <a href="http://docs.camunda.org/latest/guides/user-guide/#process-applications-the-processesxml-deployment-descriptor">processes.xml</a> file</li><li>&nbsp;make a subclass of<span style="font-family: &quot;Courier New&quot;,Courier,monospace;"> org.camunda.bpm.extension.osgi.application.OSGiProcessApplication</span></li><li>export it as OSGi service </li></ol>After that the process will be deployed and the engine will be started and exported.<br />To show you how easy it can be I created an <a href="https://github.com/rbraeunlich/camunda-bpm-processapplication-example">example project</a>.<br /><br />Please note that the feature is right now only usable when using Blueprint.<br />Also you'll have to build <a href="https://github.com/camunda/camunda-bpm-platform">camunda-bpm-platform</a> and <a href="https://github.com/camunda/camunda-bpm-platform-osgi">camunda-bpm-platform-osgi</a> yourself. But the next releases should be right around the corner ;-)<br /><br />Unfortunately, I wasn't able to activate the process application local scan for process definitions (see <a href="http://docs.camunda.org/latest/api-references/deployment-descriptors/#tags-process-archive-configuration-configuration-properties">here</a>). I couldn't figure out a way to find resources inside an embedded jar.<br />Neil Bartlett <a href="https://twitter.com/nbartlett/status/515077421859872769">mentioned</a> the BundleWiring class. Seems like I have to wait until we upgrade the project to OSGi 4.3.<br />If anyone knows a way please let me know.<br /><br />So, enjoy the OSGiProcessApplication and give me some feedback if you want to!Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com0tag:blogger.com,1999:blog-2497944031743543766.post-48303765084222511472014-06-17T14:09:00.000+02:002014-06-17T14:09:37.679+02:00camunda BPM Platform OSGi 1.0.0 releasedToday I am happy to announce the version 1.0.0 release of the camunda BPM Platform OSGi project.<br />Especially because I am the maintainer of the project ;-)<br /><br />The project started on 17th of November when we moved the "old" OSGi module out of the core platform and made it a community project.<br />So let's start with a review.<br /><br /><h3>What did we do?</h3><h3>&nbsp;</h3>First of all we now have a lot more test coverage. At the beginning there were zero tests and now we should have roughly 80% test coverage across all modules. Next to the tests there was a lot of refactoring to have smaller classes with clear responsibilities (that's what refactoring is all about, right? ;-))<br /><br />Secondly, we have a Apache Karaf feature.xml and a Blueprint example project.<br /><br />The first contribution from "outside" was the Apache Karaf commands module, which was developed by <a href="https://github.com/elek">Elek</a> from <a href="http://dpc.hu/">DCP Consulting</a>. Thank you, again!<br /><br />Finally, there is the new OSGiELResolver, which was included a few weeks ago. The new ELResolver gives us some independence from Blueprint.<br /><br />As you can see, we did quite a few things, but there are still some tasks left.<br /><br /><h3>What's left to do?</h3><h3>&nbsp;</h3>The ToDo-list states the following:<br /><ol><li>adapt Process Application API for OSGi</li><li>camunda webapp WAB (cockpit, tasklist, admin)</li><li>create example for configuring engine using PAX-CDI</li></ol>Number one Daniel, Roman and I tried to solve in May. All the results are in the platfrom-api-hack branch. They still need some review.<br />Number two and three are still open.<br /><br />That's what's left on the ToDo-list, but what else is there to do?&nbsp; <br /><br /><h3>The future </h3><h3></h3>Of course it would be great to get some feedback from "real world" users and I hope more people will use the OSGi module in the future.<br />Then the open ToDos got to get done and I guess we'll find some ideas for the future (maybe Apache ServiceMix with camunda BPM).<br /><br />After we've taken a look at the past, the present and the future there is only one thing left: <br /><br /><h3>Finally</h3><h3>&nbsp;</h3>A big "thank you" to <a href="http://camunda.org/community/team.html#daniel-details">Daniel</a> and <a href="http://camunda.org/community/team.html#roman-details">Roman</a> for guidance, support and having time for a hackathon with me! It is a pleasure working with you!Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com0tag:blogger.com,1999:blog-2497944031743543766.post-42792911700418729732014-05-25T14:50:00.000+02:002014-05-25T14:54:14.562+02:00Consuming arbitrary remote services with the OSGiELResolver (camunda BPM OSGi)In my last blog post I promised to give a slightly more advanced example about how to use the new OSGiELResolver. And as I promised, here it is ;-) <br /><br /><h3>Prerequisites</h3><br />The setup is quite simple. We have three bundles:<br /><ol><li>API</li><li>Service Provider</li><li>Service Consumer</li></ol>You can find all the sources <a href="https://github.com/rbraeunlich/camunda-bpm-osgielresolver-example">here</a>. (feel free to suggest improvements, possible bugs, etc.)<br />As runtime I used two Apache Karaf instances on my computer (version 2.3.5; I had some problems with 3.0.1).<br />For remoting we'll use <a href="http://cxf.apache.org/">Apache CXF</a> 1.4 (single bundle release).<br />And of course we'll need <a href="https://github.com/camunda/camunda-bpm-platform-osgi">camunda BPM platform OSGi</a>, which you'll have to build yourself.<br />Before I tell you more about the three bundles I'd like to point the book "<a href="http://www.manning.com/cummins/">Enterprise OSGi in Action</a>" out. Without that great book I couldn't have provided this example. It's definitely worth reading.<br /><br />So, enough advertisement, let's take a look at the bundles.<br /><br /><h3>API bundle</h3><h3>&nbsp;</h3>The API bundle is really simple. It only contains one interface with a method. We'll need the bundle in both runtimes.<br /><br /><h3>Provider bundle</h3><h3>&nbsp;</h3>Now we're getting a little bit more serious. The provider bundle contains the service implementation we want to use.<br />The context.xml contains the important parts for remoting:<br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&lt;entry key="service.exported.interfaces"&nbsp;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;value="de.blogspot.wrongtracks.osgielresolver.api.SomethingService"/&gt;&lt;entry key="service.exported.configs"<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; value="org.apache.cxf.ws"</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&lt;entry key="org.apache.cxf.ws.address"<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; value="http://localhost:9001/somethingservice"/&gt;</span><br /><br />"service.exported.interfaces" should be obvious.<br />"service.exported.configs" tells Distributed OSGi to look for implementation specific properties.<br />Lastly "org.apache.cxf.ws.address" lets us define an alternative address. It is quite helpful if you don't want to type the fully qualified name of the class in your browser or other config files.<br /><br /><h3>Consumer bundle</h3><h3>&nbsp;</h3>Let's take a look at the consumer. This bundle needs a little bit more information to work properly. To be able to consume remote services we need the OSGI-INF/remote-service/remote-services.xml. It doesn't have to be that name or that directory. You can specify the path inside the bundle with the "Remote-Service" header, which I set in the POM to:<br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp; &nbsp;&nbsp; &lt;Remote-Service&gt;OSGI-INF/remote-service/*.xml&lt;/Remote-Service&gt;</span><br /><span style="font-family: inherit;">I won't walk you through the remote-services.xml. I'm sure you'll find better explanations somewhere else. (e.g. in Enterprise OSGi in Action ;-) )</span><br /><br /><span style="font-family: inherit;">After we configured this we can use the reference tag in the context.xml to find the service.</span><br /><span style="font-family: inherit;">To make the service work with the OSGiELResolver we have to add two things. In the remote-services.xml the property "processExpression" has to be set and in the context.xml we have to use a filter.</span><br /><span style="font-family: inherit;">As you may know the ELResolver uses the filter to search for classes. Searching only worked when both, attribute and filter, were set. </span><br /><br /><h3>The provider Karaf</h3><h3>&nbsp;</h3>Like I said, I used Karaf as runtime. The "provider" Karaf needs three bundles:<br /><ol><li>API</li><li>Provider</li><li>Apache CXF</li></ol>Just drop them into the deploy directory. It worked best for me when I started them in the order API, CXF and provider. Then everything should work as expected. <br /><br /><h3>The consumer Karaf</h3><h3>&nbsp;</h3>The "consumer" Karaf needs a little bit more bundles (and if you run it on the same machine you'll have to change three ports). You have to add:<br /><ol><li>API</li><li>Consumer</li><li>Apache CXF</li><li>camunda BPM platform OSGi and dependencies</li></ol>Drop API, consumer and CXF jars into deploy (again, starting API, CXF and then consumer works best). Adding camunda BPM platform OSGi isn't very difficult because there is a feature.xml (assumed it is installed in your local Maven repository).<br />To install it type:<br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">features:addurl mvn:org.camunda.bpm.extension.osgi/camunda-bpm-karaf-feature/1.0.0-SNAPSHOT/xml/features</span><br /><br />and then:<br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">features:install camunda-bpm-karaf-feature-minimal</span> <br /><br />This should resolve all you bundles. Now, If you start the consumer bundle you should see the log saying "Started process". Strangely the logger of the service implementation was quiet. But if you uncomment the exception you can see that the service was called.<br /><br />So, as you can see, the new OSGiELResolver makes it possible to consume arbitrary remote services, which is quite an improvement. I hope my example is understandable and helps to see the possibilities.<br /><br /><h3>Hint</h3><h3>&nbsp;</h3>When you encounter this exception:<br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">java.lang.IllegalStateException: Invalid BundleContext</span><br />just start the CXF bundle again, then it should work.<br /><br />Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com0tag:blogger.com,1999:blog-2497944031743543766.post-42852927640936310422014-05-19T18:18:00.001+02:002014-05-19T18:18:48.695+02:00camunda BPM OSGi: the new OSGiELResolver<h3>Introduction </h3><br />Some of you may know that I am the maintainer of the <a href="https://github.com/camunda/camunda-bpm-platform-osgi">camunda BPM OSGi</a> project.<br />Several weeks ago I started to implement a new ELResolver (EL = expression language) and because it's finished now I want to do some shameless self-advertising for my work ;-)<br /><br /><h3>The problem</h3><br />The "old" ELResolver had some limitations: It could only work with one kind of classes (those who implement the <span style="font-family: &quot;Courier New&quot;,Courier,monospace;">JavaDelegate</span> interface) and you had to register the ELResolver as service listener.<br />Also, the implementation depends on Blueprint because it used the registered component id to find the classes.<br /><br /><h3>The new OSGiELResolver</h3><br />The new OSGiELResolver doesn't have those limitations. You can use it theoretically with every class and it doesn't depend on Blueprint. If you want to know more, please have a look at the updated <a href="https://github.com/camunda/camunda-bpm-platform-osgi/blob/master/README.md">README</a>. I would be happy if you could give me some feedback or ideas for improvement.<br /><br />So far for now. I'll try to put together a more advanced example, soon.<br /><br />Please note: this change breaks the API because I moved some classes, so this version would be a new major version number, if it weren't for the snapshot ;-)Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com1tag:blogger.com,1999:blog-2497944031743543766.post-68722235414556943762014-05-10T18:50:00.000+02:002014-05-10T18:50:40.763+02:00First steps with Apache ACE<h2><span style="font-size: x-large;">Introduction</span></h2><br />"Apache ACE is a software distribution framework that allows you to centrally manage and distribute software components, configuration data and other artifacts to target systems." (from https://ace.apache.org/)<br />Well, that sounds good enough to try it out, at least for me.<br />I like the idea to centrally configure deployments with different version and have a way to automatically distribute those.<br /><br /><h2><span style="font-size: x-large;">Starting ACE</span></h2><br />Setting up Apache ACE was pretty easy. The&nbsp;<a href="https://ace.apache.org/user-doc/getting-started.html">Getting started guide</a> contains all the necessary steps.<br />My MacBook was the ACE server and my Raspberry Pi a target. <br /><br />Using the Web GUI is easy and straightforward (nice one&nbsp;<a href="https://ace.apache.org/get-involved/project-team.html">guys</a> ;-) ).<br />But that's for little children. I wanna find a way to automate everything with scripts.<br /><br /><h2><span style="font-size: x-large;">The Client Shell API</span></h2>Basically there are two ways to talk to the server remotely. One is the <a href="https://ace.apache.org/user-doc/shellapi.html">Client Shell API</a> and the other way is via <a href="https://ace.apache.org/user-doc/shellapi.html">REST API</a>. For now I'll stick with the Shell API<br /><br /><h3>1st step: connecting to the server as shell client</h3>Before we can write a script we have to connect to the server.<br />With some help from the iQSpot people (see <a href="http://developer.iqspot.fr/2014/archi-part-2/">here</a>) I figured it out.<br />They suggest starting the client like this:<br /><br /><pre><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">java -Dagent.discovery.serverurls="http://<i>server</i>:<i>port</i>"<br /> -Dorg.apache.ace.server="<i>server</i>:<i>port</i>"<br /> -Dorg.apache.ace.obr="<i>server</i>:<i>port</i>"<br /> -Dorg.osgi.service.http.port=-1<br /> -jar client.jar</span></pre><pre><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;</span></pre><span style="font-family: inherit;">Unfortunately, that</span> didn't work for me (even after adding some missing backslashes).<br />The default is that you should be in the directory of client.jar. "-jar client.jar" wasn't the problem.<br />The startup searches for the client/conf directory, so when you see this exception:<br /><br /><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">java.lang.IllegalArgumentException: Bad arguments; either not an existing directory or an invalid interval.</span></span><br /><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; at org.apache.ace.configurator.Configurator.&lt;init&gt;(Configurator.java:89)</span></span><br /><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; at org.apache.ace.configurator.Activator.init(Activator.java:33)</span></span><br /><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; at org.apache.felix.dm.DependencyActivatorBase.start(DependencyActivatorBase.java:76)</span></span><br /><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; at org.apache.felix.framework.util.SecureAction.startActivator(SecureAction.java:645)</span></span><br /><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; at org.apache.felix.framework.Felix.activateBundle(Felix.java:2146)</span></span><br /><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; at org.apache.felix.framework.Felix.startBundle(Felix.java:2064)</span></span><br /><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; at org.apache.felix.framework.Felix.setActiveStartLevel(Felix.java:1291)</span></span><br /><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; at org.apache.felix.framework.FrameworkStartLevelImpl.run(FrameworkStartLevelImpl.java:304)</span></span><br /><span style="font-size: x-small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; at java.lang.Thread.run(Thread.java:722)</span></span><br /><br />You're probably starting the client from a different directory.<br />To get rid of that exception we have to set a property:<br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">-Dorg.apache.ace.configurator.CONFIG_DIR=</span><br />All in all the command to start the client looks like this:<br /><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">java -Dagent.discovery.serverurls="http://<i>server</i>:<i>port</i>"\</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp; -Dorg.apache.ace.server="<i>server</i>:<i>port</i>"\<br />&nbsp;&nbsp;&nbsp;&nbsp; -Dorg.apache.ace.obr="<i>server</i>:<i>port</i>"\<br />&nbsp;&nbsp;&nbsp;&nbsp; -Dorg.osgi.service.http.port=-1\<br />&nbsp;&nbsp;&nbsp;&nbsp; -Dorg.apache.ace.configurator.CONFIG_DIR="apache-ace-2.0.1-bin/client/conf"\<br />&nbsp;&nbsp;&nbsp;&nbsp; -jar apache-ace-2.0.1-bin/client/client.jar</span><br /><span style="font-family: inherit;"><br /></span><span style="font-family: inherit;">Now we can start the client.</span><br /><span style="font-family: inherit;">But to pass a script to the shell we need two more arguments. Thanks again to the iQSpot people. They already pointed out those arguments:</span><br /><ul><li><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-size: 14px; line-height: 1.5em;">-Dgosh.args="–</span><span style="font-size: 14px; line-height: 1.5em;">-args</span>"</span></span></li><li><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">-Dace.gogo.script.delay=<i>delay</i></span></li><li><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">-Dace.gogo.script=/path/to/script.gogo</span></li></ul><span style="font-family: inherit;">What do those three do? </span><br /><span style="font-family: inherit;">Everything you'll pass as "gosh.args" will be executed immidiately. If pass "--help" for example and start the client you'll see the help output.</span><br /><span style="font-family: inherit;">The delay is helpful when you want to give your client some time to synchronize with the server.</span><br /><span style="font-family: inherit;">"ace.gogo.script" should be obvious ;-)</span><br /><span style="font-family: inherit;">We end up with the following command:</span><br /><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">java -Dagent.discovery.serverurls="http://<i>server</i>:<i>port</i>"\</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp; -Dorg.apache.ace.server="<i>server</i>:<i>port</i>"\<br />&nbsp;&nbsp;&nbsp;&nbsp; -Dorg.apache.ace.obr="<i>server</i>:<i>port</i>"\</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp; -Dorg.osgi.service.http.port=-1\<br />&nbsp;&nbsp;&nbsp;&nbsp; -Dorg.apache.ace.configurator.CONFIG_DIR="apache-ace-2.0.1-bin/client/conf"\<br />&nbsp;&nbsp;&nbsp;&nbsp; -Dace.gogo.script.delay="3000"\<br />&nbsp;&nbsp;&nbsp; &nbsp;-Dace.gogo.script="script.foo"\ </span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp; -jar apache-ace-2.0.1-bin/client/client.jar</span><br /><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: inherit;">Now we have to find out, what we should put into "script.foo".</span></span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: inherit;">&nbsp;</span> </span><br /><h3><span style="font-family: inherit;">Shell commands</span></h3><h3><span style="font-family: inherit;"><br /></span></h3><span style="font-family: inherit;">Every (basic) command is described </span><a href="https://ace.apache.org/user-doc/shellapi.html">here</a>.<br />The steps are quite simple: cw, ca, cf, ca2f, cd, cf2d<br />If you don't like or get the abbreviations (it took me a while) there is also a nice picture in the <a href="https://ace.apache.org/user-doc/restapi.html">REST API documentation</a>:<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://cwiki.apache.org/confluence/download/attachments/27822075/entities.png?version=1&amp;modificationDate=1311923466000" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://cwiki.apache.org/confluence/download/attachments/27822075/entities.png?version=1&amp;modificationDate=1311923466000" /></a></div>What the picture is missing is cw or "create workspace". When using the Shell API you need a workspace, which you can commit later.<br /><br /><h3>The script</h3><h3>&nbsp;</h3><span style="font-family: inherit;">So, what should skript.foo do? Let's assume we have to upload some generated artifacts from our CI server&nbsp;</span><br /><span style="font-family: inherit;">The steps are</span><br /><ol><li><span style="font-family: inherit;">create workspace</span></li><li><span style="font-family: inherit;">add the new Jars as artifacts from certain directory</span></li><li><span style="font-family: inherit;">create a new feature</span></li><li><span style="font-family: inherit;">add artifacts to feature</span></li><li><span style="font-family: inherit;">create a new distribution</span></li><li><span style="font-family: inherit;">add new feature and existing ones to distribution</span></li><li><span style="font-family: inherit;">add feature to existing target&nbsp;</span></li></ol><span style="font-family: inherit;">I have to admit that it took me quite a while to figure everything out because I'm not very experienced with Apache Felix GoGo.</span><br /><span style="font-family: inherit;">Creating the workspace is easy: <span style="font-family: &quot;Courier New&quot;,Courier,monospace;">w = (cw)</span></span><br /><span style="font-family: inherit;">Now we can call the workspace with $w. Adding the Jars was more difficult. Let's assume the directory is ./toAdd. Then the command looks like this:&nbsp; </span><br /><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">each ([(ls toAdd)]) {$w ca (($it toURL) toString) false}</span><br /><span style="font-family: inherit;"><br /></span><span style="font-family: inherit;">You "toAdd" can be changed to any path and you could use some wildcards, like <span style="font-family: &quot;Courier New&quot;,Courier,monospace;">ls toAdd/*.jar</span> </span><br /><span style="font-family: inherit;">I guess if you're used to GoGo the command won't be a surprise. If you're not used to it, I would like to explain the different parts to you:</span><br /><span style="font-family: inherit;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">each</span> takes a list and a function. <span style="font-family: &quot;Courier New&quot;,Courier,monospace;">ls toAdd</span> returns a File array. That's why we need the brackets. They convert the array into a list. After that comes the function, indicated by the braces.</span><br /><span style="font-family: inherit;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">$w ca</span> is the method to create an arfifact.</span><span style="font-family: inherit;"> <span style="font-family: &quot;Courier New&quot;,Courier,monospace;">$it</span> is the iterator over the list that is provided by <span style="font-family: &quot;Courier New&quot;,Courier,monospace;">each<span style="font-family: inherit;">.</span></span></span><br /><span style="font-family: inherit;">Then we call the methods <span style="font-family: &quot;Courier New&quot;,Courier,monospace;">toURL</span> and <span style="font-family: &quot;Courier New&quot;,Courier,monospace;">toString</span> because reflection makes it possible ;-)</span><br /><br />Third step: add all artifacts to feature<br /><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">each ($w la "(Bundle-SymbolicName=org.camunda.*)") {symbolicName=($it getAttribute "Bundle-SymbolicName"); $w ca2f "(Bundle-SymbolicName="$symbolicName")" "(name=test-feature)"}</span><br /><br />Again, we use a for-each-loop.<br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">$w la</span> lists all the bundles that match the passed pattern. (Here, I want to add all camunda bundles, no advertisement ;-))<br />Then I save the symbolic name in a variable, so it's easier for me later to reference it. <br />org.apache.ace.client.repository.RepositoryObject has a getAttribute method, which we use here.<br />Also, please note the semicolon.<br />We use the symbolic name as part of the first argument for ca2f (create artifact2feature).<br />The String contains three parts<br /><ul><li><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">"(Bundle-SymbolicName="</span></li><li><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;</span><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">$symbolicName</span></li><li><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">")"</span> </span></li></ul>I don't know why, but we don't ne a "+" for string concatenation. The second argument is the name of the feature. I just assume it to stay the same: "test-feature"<br />Creating a distribution and a feature2distribution are nothing special.<br /><br />All in all we end up with the following:<br /><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">w = (ace:cw)<br />$w cf test-feature<br />$w cd test-distro<br /><br />each ([(ls toAdd)]) {$w ca (($it toURL) toString) false}<br /><br />each ($w la "(Bundle-SymbolicName=org.camunda.*)") {symbolicName=($it getAttribute "Bundle-SymbolicName"); $w ca2f "(Bundle-SymbolicName="$symbolicName")" "(name=test-feature)"}<br /><br />$w cf2d "(name=test-feature)" "(name=test-distro)"<br /><br />$w commit</span><br /><br />That should do the trick so far. <br />Stay tuned for my next steps with ACE ;-)<br /><ol></ol>Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com0tag:blogger.com,1999:blog-2497944031743543766.post-55440585767807780322014-05-06T21:51:00.000+02:002014-05-06T21:51:24.731+02:00Glassfish 4, Commons Mail and "UnsupportedDataTypeException: no object DCH for MIME type multipart/mixed"I know there are a bazillion posts/threads/etc. about the exception mentioned in the title and now there are a bazillion + one ;-)<br />Unfortunately I couldn't find the solution I want to present to you anywhere else.<br /><br />First some context:<br />My <a href="https://github.com/rbraeunlich/ProSt/blob/master/modules/prost-example-process/src/main/java/de/blogspot/wrongtracks/prost/example/behaviour/SendMailWithAttachmentBehaviour.java">class</a> extends an Activiti class and uses Apache Commons Mail to send an email.<br />The email contains some text and has a file (txt/pdf/docs) attached.<br />Everything runs inside a Glassfish 4 and the Jars are deployed as OSGi bundles.<br /><br />When calling email.send() the server threw the feared UnsupportedDataTypeException:<br /><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">Caused by: javax.activation.UnsupportedDataTypeException: no object DCH for MIME type multipart/mixed; <br />&nbsp;&nbsp;&nbsp; boundary="----=_Part_0_397989068.1398665205325"<br />&nbsp;&nbsp;&nbsp; at javax.activation.ObjectDataContentHandler.writeTo(DataHandler.java:891)<br />&nbsp;&nbsp;&nbsp; at javax.activation.DataHandler.writeTo(DataHandler.java:317)<br />&nbsp;&nbsp;&nbsp; at javax.mail.internet.MimeBodyPart.writeTo(MimeBodyPart.java:1574)<br />&nbsp;&nbsp;&nbsp; at javax.mail.internet.MimeMessage.writeTo(MimeMessage.java:1840)<br />&nbsp;&nbsp;&nbsp; at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1119)<br />&nbsp;&nbsp;&nbsp; ... 120 more </span><br /><br />Like I mentioned the Internet is full of solutions but none of them worked for me.<br />Because the Glassfish showed me that all of my bundles were correctly linked and resolved the problem had to be another place.<br /><br />My colleague then told me I should try to change the TCCL. After some try-and-error it worked (I tried the one from commons.mail, the one from javax.activation and one I forgot ;-)).<br /><br />The solution was to import javax.mail in my bundle and change the TCCL to the javax.mail classloader:<br /><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">Thread.currentThread().setContextClassLoader(javax.mail.Message.class.getClassLoader());</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">email.send()</span><br /><br />I am not sure why only the javax.mail classloader works. For me it is some arcane dependency/classloading/visibility problem.<br />Nevertheless, I hope this post helps some Glassfish/OSGi users.<br /><br />Finally, I would like to thank my colleague&nbsp;<a href="https://twitter.com/spost1970">@spost1970</a> for helping me find a solution.Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com0tag:blogger.com,1999:blog-2497944031743543766.post-59679409368752461812014-04-26T10:07:00.000+02:002014-04-26T10:12:01.935+02:00Integrating Apache Aries blueprint into Glassfish 4After experimenting with Glassfish 4 lately I would like to let you know what I am up to. It's nothing big (yet) ;-) <br /><br /><h3>A little bit of background</h3><h3></h3>Glassfish comes with integrated OSGi support (Apache Felix) but without Blueprint (as far as I know). So putting a Blueprint container into Glassfish became my task.<br /><br />The nice thing about Glassfish is that it combines Java EE (especially EJBs) and OSGi and that, in contrast to JBoss, it has a nice OSGi web console.<br />If you want to get to know more about the OSGi-JEE combination the keyword for your favourite search engine is "fighterfish".<br /><br />Most of the credit goes to Yong Tang and his <a href="http://osgizone.typepad.com/tangyong/2013/04/glassfish-osgi-integration-part1-integrating-apache-aries-application-into-glassfish-v4.html">blog entry</a>. He describes the integration of Aries Application but also describes the basic parts necessary for my task.<br /><br /><h3>The problem </h3><br />So, before we start, what is the problem? If you're familiar with Glassfish you certainly know there is an autodeploy/bundles directory. Why don't I just drop the necessary bundles into the autodeploy/bundles directory?<br />When using the autodeploy directory Glassfish doesn't start the bundles so you'll have to do it by hand every time you empty the osgi-cache directory and, of course, initially.<br />But there's a more convenient way.<br /><br /><h3>Let's get it on</h3><br />What's the better way?<br />Just drop the jars<br /><ul><li>Aries Blueprint Api(v1.0.0)</li><li>Aries Blueprint(v1.1.0)</li><li>Aries Proxy (v1.0.0)&nbsp;</li><li>Aries Util(v1.0.0)</li></ul>into glassfish/modules/autostart.<br />To make sure all dependencies are there add slf4j api (v1.7.2), logback core(v1.0.13) and logback classic (v1.0.13) (or whichever logging framework you prefer). You don't need any additional configuration because we didn't create a subdirectory.<br /><br />See, piece of cake. The trick is to find the right directory. Now the Blueprint extender will do its job right after start up. <br /><div id="stcpDiv" style="left: -1988px; position: absolute; top: -1999px;">glassfish/modules/autostart<br /><div id="stcpDiv" style="left: -1988px; position: absolute; top: -1999px;">glassfish/modules/autostart<br /><div id="stcpDiv" style="left: -1988px; position: absolute; top: -1999px;">glassfish/modules/autostart</div></div></div><div id="stcpDiv" style="left: -1988px; position: absolute; top: -1999px;">glassfish/modules/autostar</div><div id="stcpDiv" style="left: -1988px; position: absolute; top: -1999px;">glassfish/modules/autostar</div>Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com2tag:blogger.com,1999:blog-2497944031743543766.post-69070046640092390032014-01-22T19:39:00.000+01:002014-01-22T19:44:52.210+01:00Activiti/camunda BPM: custom behavior and BPMN extension elements using Blueprint<h3>Introduction&nbsp;</h3><h3>&nbsp;</h3>In the last few weeks I have been working on a problem regarding OSGi-Blueprint and Activiti. Because it wasn't as easy as I would have hoped I want to share my solution with you. I will start by explaining my environment, show you the problem and then I will explain my first attempt. After that I will present my solution. Finally I will show some ideas how to make it better and things that I did not test.<br /><br />Just a short hint about the writing: when I reference a class or some XML it's written in italic, e.g. <i>Object</i>. When you see "process engine", I am talking about the whole thing, but when you see <i>ProcessEngine</i> it's the actual class.<br /><br /><h3>My environment</h3><h3>&nbsp;</h3>I use the Activiti-framework in version 5.12.1, Apache Aries in version 1.0.0 with a little modification and my own ProSt bundles. ProSt can be found <a href="https://github.com/rbraeunlich/ProSt">here</a>. The README.md explains why and what I changed in Aries.<br /><br />I haven't tried, yet, but I am pretty sure that camunda BPM suffers the same problem because<br />both share the same <i>MailActivityBehavior</i> and <i>BlueprintELResolver</i> classes and use <i>&lt;extension-elements&gt;</i> for injection. So if you prefer camunda and see "activiti" somewhere you just have to replace it with "camunda" in your head ;-)<br /><br /><h3>The problem&nbsp;</h3><h3>&nbsp;</h3>In general, I just wanted to send an e-mail during my process-execution. Sounds pretty easy, right?<br /><br />My&nbsp;<i><a href="https://github.com/rbraeunlich/ProSt/blob/master/modules/prost-example-process/src/main/java/de/blogspot/wrongtracks/prost/example/behaviour/SendMailWithAttachmentBehaviour.java">SendMailWithAttachmentBehaviour</a></i> class extends the previously mentioned <i>MailActivitiBehavior</i> class. The <a href="https://github.com/rbraeunlich/ProSt/blob/master/modules/prost-example-process/src/main/resources/OSGI-INF/activiti/testProcess.bpmn20.xml">process definition</a> contains all the necessary information to send the e-mail, e.g. from, to and subject. Only the attachment is missing, which I get from the execution environment.<br /><br />Because I use Blueprint I cannot use the<i> activit:class</i> or <i>type="mail" </i>attributes<i> </i>in the process definition. I have to declare the class this way:<br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">activiti:deleExpression="${sendMailWithAttachmentBehavior}"</span><br /><span style="font-size: x-small;"><br /></span><span style="font-size: x-small;">A little hint: the name in the braces has to match the one used as bean id in the blueprint.xml.</span><br /><br />The other ways do not work with OSGi because of class visibility etc.<br /><br />The easy part was to extend the <i>BlueprintELResolver</i> class (<a href="https://github.com/rbraeunlich/ProSt/blob/50d48c046482c68f1ad412c349d1c3463b12e5f5/modules/activiti-engine-blueprint-wrapper/src/main/java/de/blogspot/wrongtracks/prost/blueprint/ProStBlueprintELResolver.java">ProStBlueprintELResolver</a>) and add a way to add custom behavior classes at the moment.<br /><br />So, what happens when the process engine tries to resolve the expression? <br />When the bundle is loaded Blueprint creates a dynamic proxy and registers it at the <i>ProStBlueprintELManager</i>.<br />After the process reaches the <i>ServiceTask</i> which delegates to the <i>${sendMailWithAttachmentBehaviour}</i> the process-engine asks its ExpressionLanguageResolvers if they know something with the name "<span style="font-family: inherit;">sendMailWithAttachmentBehaviour". Logically the proxy is found.</span><br /><span style="font-family: inherit;">After that the process engine tries to set the extension-elements at the class.</span><br /><span style="font-family: inherit;">First it tries to find setter methods and if it cannot find setters it tries field injection. (see <i>ClassDelegate.applyFieldDeclaration()</i>)</span><br /><span style="font-family: inherit;">Both ways do not work.</span><br /><span style="font-family: inherit;">But why?</span><br /><span style="font-family: inherit;">Of course a proxy does not have any fields. But why is it not possible to just add the setters to the <i>SendMailWithAttachmendBehaviour</i> class?</span><br /><span style="font-family: inherit;">The call is <i>proxy.getClass().getMethods()</i> and according to the documentation this will return all the methods of the interfaces that the proxy was created with. <i>ActivityBehavior</i> does not declare the <i>setSubject() </i>etc.<i> </i>methods because they are only needed for e-mails.</span><br /><br /><h3>First attempt</h3><h3>&nbsp;</h3>At first I thought the solution was quite obvious. I would just export a second interface containing the setters like this:<br /><br /><pre><div class="line" id="LC11"><br /><span class="nt">&lt;bean</span> <span class="na">id=</span><span class="s">"sendMailWithAttachment"</span><br /><span class="na">class=</span><span class="s">"de.blogspot.wrongtracks.prost.example.behaviour.SendMailWithAttachmentBehaviour"</span> <span class="nt">/&gt;</span><br /></div><br /><div class="line" id="LC13"><br />&nbsp;<span class="nt">&nbsp;</span><br /><br /><span class="nt">&lt;service</span> <span class="na">ref=</span><span class="s">"sendMailWithAttachment"&gt;</span><br /><br /><span class="s">&nbsp; &lt;interfaces&gt;</span><br /><br /><span class="s"> &lt;value&gt;</span><span class="s">org.activiti.engine.impl.pvm.delegate.ActivityBehavior&lt;/value&gt; </span><br /><br /><span class="s"> &lt;value&gt;de.blogspot.wrongtracks.prost.example.behavior.ExtensionElementsMailSetter&lt;/value&gt; </span><br /><br /><span class="s">&nbsp; &lt;/interfaces&gt;</span><br /><br /><span class="s">&lt;/services&gt; </span></div><br /></pre>But wait, if you take a look at the <a href="https://github.com/rbraeunlich/ProSt/blob/5712d03753b40d77c1f519f6691d326fc9401e8f/modules/activiti-engine-blueprint-wrapper/src/main/resources/OSGI-INF/blueprint/context.xml">(old) context.xml</a> you can see that my reference listener just listens for <i>ActivityBehavior</i> and not the other interface. That's why the created proxy won't contain the methods from the other interface. Too bad...<br /><br /><h3>The solution</h3><h3>&nbsp;</h3>I found the solution accidentally while reading the Apache Aries Blueprint documentation. <a href="http://aries.apache.org/modules/blueprint.html#reference-lists">This chapter</a> points out that you can also listen for service references.<br />I changed the methods to accept a <i>ServiceReference</i> instead of a <i>ActivitiyBehavior</i> and when the expression should be resolved I use the <i>BundleContext</i> to get the service. At that point it is not a proxy, it is the implementation.<br /><br />Then everything works just fine. I don't even need the setter interface anymore.<br />You can see the solution when you look at the&nbsp;<a href="https://github.com/rbraeunlich/ProSt/blob/master/modules/activiti-engine-blueprint-wrapper/src/main/resources/OSGI-INF/blueprint/context.xml">new context.xml</a> and the previously mentioned&nbsp;<i><a href="https://github.com/rbraeunlich/ProSt/blob/master/modules/activiti-engine-blueprint-wrapper/src/main/java/de/blogspot/wrongtracks/prost/blueprint/ProStBlueprintELResolver.java">ProStBlueprintELResolver</a></i>. (the previously showed link pointed to an old version so nothing would be spoiled ;-) )<br /><br />That's it, that is my solution to add custom behavior to the process engine and use extension elements in the BPMN XML.<br /><br /><h3>How could we improve the whole thing?</h3><h3>&nbsp;</h3>Strangely, I have no idea how the whole thing could be improved. I would like to hear your ideas. Also, I would like to know if you think that the way presented here is good or bad or something in between.<br /><br /><h3>What didn't I try?</h3><h3>&nbsp;</h3>You should note that I have not tried to find out how <i>JavaDelegates</i> behave in the same situation. I just did not have time and I wanted to show you my solution as soon as I finished it.Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com0tag:blogger.com,1999:blog-2497944031743543766.post-76184568407041215332013-11-20T22:08:00.000+01:002013-11-20T22:08:31.870+01:00JBoss 7.2: From Aries to Gemini and back againI have been using the combination of JBoss 7.2 and Apache Aries for a while now.<br />Recently, I had to face a problem, which I had been ignoring for quite a while successfully.<br /><br />When you use Aries blueprint you cannot use setters with non-void return values. Aries expects setters to have "void" as their return type.<br />Because fluent interfaces are quite popular at the moment that was a problem for me.<br />The Aries people already have an open issue to fix/improve this and maybe the blueprint specification also wants setters to have that return type. I don't want to start a discussion about this here.<br /><br />Nevertheless, I wanted to use the setter. I could have written a subclass with a slightly different setter, which just calls the setter I really want to use. But that would've meant to write a new setter for every existing one. So that was not an option.<br /><br />Another option was to change the Aries source code by myself (what I should have done in the first place). But I didn't know how complex that would be so I chose the third option.<br /><br />The option I chose was to replace Aries with Gemini. At some point during my research I found a post, which stated that Gemini could handle non void return values for setters.<br />After I managed to collect all of Gemini's dependencies and place them in JBoss' bundle directory I thought I reached my goal. But when I started the server it didn't go past the registration of my EJBs. The server log just showed a NPE some seconds before when trying to release some lock.<br /><br />I wasn't sure what to do because the NPE wasn't really helpful and I couldn't imagine what the problem was. My only idea was to remove the Jar containing the EJBs.<br />After that the server started but the Blueprint-Extender didn't start automatically. That wasn't much of a deal. I opened up the management console and started the bundles all by hand. Everything seemed to be fine. Even dropping the EJB-Jar in the deployment directory worked.<br />Unfortunately, after stopping and starting the server again I was staring at the same exception and the JBoss refused to do its work.<br />What followed was some try-and-error with more error than try. I still don't know what the problem is.<br /><br />This was really disappointing (it still is). I decided to take the option I refused before and to change Aries' code. Fortunately I had the Gemini-Jars in another local JBoss so I could just go back to the one I had used before.<br />And who could've guessed? I just had to change one line in <span class="entity-name" title="org.apache.aries.blueprint.utils.ReflectionUtils">org.apache.aries.blueprint.utils.ReflectionUtils:</span><br /><br /><pre><span class="kw">if</span>&nbsp;(<span class="mark-35#1">name</span>.<a href="http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/String.java#String.length%28%29" title="java.lang.String.length() : int">length</a>()&nbsp;&gt;&nbsp;3&nbsp;&amp;&amp;&nbsp;<span class="mark-35#1">name</span>.<a href="http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/String.java#String.startsWith%28java.lang.String%29" title="java.lang.String.startsWith(java.lang.String) : boolean">startsWith</a>(<span class="strliteral">"set"</span>)&nbsp;&amp;&amp;&nbsp;</pre><pre><span class="mark-37#1">resultType</span>&nbsp;==&nbsp;<a class="hidden" href="http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/Void.java#Void.0TYPE" title="Class TYPE">Void</a>.<span class="mark-16#1"><a class="hidden" href="http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/Void.java#Void.0TYPE" title="Class TYPE">TYPE</a></span>&nbsp;&amp;&amp;&nbsp;<span class="mark-36#1">argTypes</span>.<span class="mark-4#1">length</span>&nbsp;==&nbsp;1)</pre><pre>&nbsp;</pre><span style="font-family: inherit; font-size: small;">I just had to remov</span><span style="font-size: small;"><span style="font-family: inherit;">e the Void.TYPE and everything worked.</span></span><br /><span style="font-size: small;"><span style="font-family: inherit;">I guess next time </span></span>I'll try to change the code first ;)Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com0tag:blogger.com,1999:blog-2497944031743543766.post-59103328083006842362013-07-12T16:18:00.001+02:002013-07-25T14:37:56.187+02:00Comparison of different BPMN-modelling toolsRecently, I stumbled upon the problem that the Activiti-engine wouldn't accept a BPMN-XML-file which I modeled with camunda Modeler.<br />No error message or warning was displayed, but I could see in the log-file that parsing started.<br />The process just didn't arrive in the repository.<br /><br />Because of that, I was wondering, how good the interoperability of the different engines and tools is at the moment.<br />BPMN is standardised and should work on every tool and engine. But as usual, reality shows us, that it's different.<br /><br />This blog post covers several modelling tools and how good they work together. In a later blog post I'll feed the different BPMN-files to different engines.<br /><br />I chose the tools for no special reasons.<br />Activiti and camunda because I work with Activiti (and camunda forked it).<br />SemTalk because I do have a license for it (it's the only commercial tool I chose).<br />The rest is more or less randomly in the list and because they are available for free.<br /><br />I compared:<br /><ul><li>Activiti Modeler 1.0 (Activiti Version 5.13) </li><li>camunda Modeler 2.0.12</li><li>Yaoqiang Version 2.2.2 </li><li>Bizagi 2.5.1.1</li><li>Bonita BPM Studio 6.0.0 </li><li>MS Vision 2010/ SemTalk 4.2.0.4230</li><li>Activiti Designer&nbsp; 5.12.0</li></ul>You can find my results in a PDF, which I placed <a href="https://docs.google.com/file/d/0B6fecPw2wyR9RWJVdy1aNHZsb2s/edit?usp=sharing" target="_blank">here</a>.<br /><br />To give a short explanation of the colours used:<br /><ul><li>green -&gt; everything was alright</li><li>yellow -&gt; it worked with some problem </li><li>red -&gt; it didn't work or there were some major problems</li><li>grey -&gt; it couldn't be tested or was unnecessary.</li></ul>Bizagi is completely grey, because it has no im- or export for BPMN-XML.<br /><br />This is the example process I used: <br /><div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-m4CYXAPOoFE/UeAMrd8YqmI/AAAAAAAABMg/f3sW3iH476g/s1600/newDiagram_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="481" src="http://3.bp.blogspot.com/-m4CYXAPOoFE/UeAMrd8YqmI/AAAAAAAABMg/f3sW3iH476g/s640/newDiagram_1.png" width="640" /></a></div>I wanted the example process to be very simple. It includes the basics, which I think should be supported.<br />It was modeled with every tool and opened with every tool. When testing if editing works, I added a task to the second pool.<br /><br />Next to the result-table in the PDF, I took some notes during the tests, which I want to share:<br /><ul><li>Activiti Modeler can't display pools or lanes</li><li>Activiti Modeler can't display message flows</li><li>Activiti Modeler needs a Tomcat server (or similar), because it's part of Activiti Explorer</li><li>Yaoqiang would work better with some guides</li><li>camunda Modeler needs Eclipse, because it's a plug-in</li><li>modelling a flow (arrow) in SemTalk is too complicated</li><li>Yaoqiang, because of it's page layout, becomes confusing with big diagrams</li><li>Bizagi, Bonita and Yaoqiang validate the model</li><li>To show message passing, Bonita forces you to explicitly model sender and receiver</li><li>for Activiti Modeler, every file hat to be a .bpmn20.xml and not just .bpmn&nbsp;</li><li>Activiti Designer can't display message flows </li></ul><br />I don't want to name a winner, because I depends on what you're working on.<br />Some modelling tools are embedded (like Activiti Modeler and Bonita) and some are standalone (like Yaoqiang) and there are different use-cases and environments in which you would prefer one or the other tool.<br /><br />But I got to admit, that I am a little bit disappointed by Activiti Modeler. It can't work with any of the the BPMN-files the other tools exported.<br /><br />I hope my results will be helpful for people who have to chose a modelling tool and I hope that some of the tool-developers will try to improve their tools because of my results. Have fun ;)<br /><br /><b>Update: </b>I added the results for Activiti Designer to the PDF. It works slightly better than the Modeler but could be better.Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com12tag:blogger.com,1999:blog-2497944031743543766.post-78458552658993672962013-06-04T17:29:00.004+02:002013-06-04T17:29:49.848+02:00Using the Activiti-Engine as bundle in JBoss AS 7.2<h3><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">Introduction</span><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"> </span></h3><h3><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><br /></span></h3><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">I am quite new to OSGi and when trying to deploy and use the Activiti-Engine as OSGi bundle in JBoss (I'll usw "JBoss" as abbreviation for JBoss AS 7.2) I have had some problems.&nbsp;</span><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">The only example about OSGi and Activiti I was able to find was the one from </span><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;"><a href="http://www.manning.com/rademakers2/" target="_blank">Activiti in Action</a></span></span> </span>which uses Apache Karaf. Of course those environments differ a lot.</span><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">So I wanna share my knowledge with you. </span><br /><h3><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">&nbsp;</span></h3><h3><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">Motivation&nbsp;</span></h3><h3><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">&nbsp;</span></h3><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">Before we start, I'd like explain why I want to use the combination of Activit, OSGi and JBoss. I won't argue about the pros and cons of using OSGi, there is enough literature about that.</span><br /><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">The fact that Activiti scans every new bundle if it contains a process definition will become very helpful for future processes.</span><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">Also, updating a process definition by replacing a single bundle and not replacing </span><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">e.g. </span>a whole WAR-file, seems like an advantage.</span><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><br /></span><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">But, why do I want to use JBoss and not e.g. Karaf?</span><br /><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">When JBoss changed to version 7 they restructured their whole server to use OSGi/JBoss modules. So, OSGi is a part of JBoss.</span><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">Last time I checked, JBoss was the only fully JEE 6 compliant server with OSGi integration and because I need EJBs for client - server communication I need JBoss. (At this point, I have no idea how to combine EJBs and OSGi but I'll try to figure that out, soon)</span><br /><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">Those are my reasons so let's see how we can get those three running. </span><br /><h3><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">&nbsp;</span></h3><h3><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">Let's start </span></h3><h4><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">&nbsp;</span></h4><h4><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">Environment</span><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"> </span></h4><h4><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><br /></span></h4><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">To make sure that everyone can easily repeat my steps, I'll start with the environment/versions:</span><br /><ul><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">JBoss AS 7.2</span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">OSGi 4.2 (included in JBoss)</span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">Activiti 5.12.1</span></li></ul><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">Please notice, that I use JBoss AS 7.2, which you have to compile yourself at the moment. With version 7.1.1 I have had a <a href="http://forums.activiti.org/content/npe-blueprint-initializing-blueprintexpressionmanager" target="_blank">problem</a>.</span><br /><ul></ul><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">I had the server running locally under Linux Mint 14.1.</span><br /><h4><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">&nbsp;</span></h4><h4><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">Bundle dependencies&nbsp;</span></h4><h4><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><br /></span></h4><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">The example from </span><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">Activiti in Activiti uses a feature.xml to resolve all the dependencies and install them from a Maven repository. Unfortunately, this involves some Karaf "magic". That's why I had to download all dependencies manually. (I also tried </span><span style="font-size: small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><br /></span></span><br /><pre><span style="font-size: small;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">mvn org.apache.felix:maven-bundle-plugin:wrap/bundleall</span></span></pre><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">on the activiti-engine and -osgi project, but that didn't work out for me)</span><br /><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">We need the following JARs (I added links to make downloading them easier):</span><br /><ul><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><a href="http://activiti.org/download.html" target="_blank">activiti-bpmn-converter-5.12.1</a></span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">activiti-bpmn-model-5.12.1</span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">activiti-engine-5.12.1</span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">activiti-osgi-5.12.1</span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><a href="http://ebr.springsource.com/repository/app/bundle/version/detail?name=com.springsource.javax.transaction&amp;version=1.1.0&amp;searchType=bundlesBySymbolicName&amp;searchQuery=javax.transaction" target="_blank">com.springsource.javax.transaction-1.1.0</a></span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><a href="http://ebr.springsource.com/repository/app/bundle/version/detail?name=com.springsource.org.apache.commons.lang&amp;version=2.4.0&amp;searchType=bundlesByName&amp;searchQuery=commons+lang" target="_blank">com.springsource.org.apache.commons.lang-2.4.0</a></span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><a href="http://sourceforge.net/projects/joda-time/files/joda-time/2.1/" target="_blank">joda-time_2.1.0</a></span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><a href="https://code.google.com/p/mybatis/downloads/detail?name=mybatis-3.1.1-bundle.zip&amp;can=2&amp;q=" target="_blank">org.mybatis.mybatis_3.1.1</a></span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><a href="http://www.slf4j.org/download.html" target="_blank">slf4j-api-1.7.5</a></span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">slf4j-nop-1.7.5</span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><a href="http://aries.apache.org/downloads/currentrelease.html" target="_blank">org.apache.aries.proxy</a>&nbsp;</span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"> org.apache.aries.blueprint</span></li><li> <span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">org.apache.aries.util</span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">org.h2 (optional)</span></li></ul><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">Downloading yourself Apache Aries seems a little bit odd because the JBoss </span><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><a href="https://docs.jboss.org/author/display/JBOSGI/Application+Server+Integration" target="_blank">documentation</a></span> states that there is blueprint support.</span><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"> Nevertheless, I</span> <span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">couldn't find, nor activate it, so I was forced to include it myself.</span><br /><br /><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">Another interesting thing is, that I couldn't start the included h2 database (com.h2database.h2). That's why I downloaded it, too. Please notice, that you will only need the h2-bundle if you want to use a h2-database (makes sense, doesn't it ;) )</span><br /><br /><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;"><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">We could throw all those bundles into the </span><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Trebuchet MS&quot;,sans-serif;">&lt;&lt;jboss-root&gt;&gt;/standalone/deployments</span> directory (if you're using JBoss in standalone-mode), but I think a better approach is to place the more general JARs in the <span style="font-family: &quot;Trebuchet MS&quot;,sans-serif;">&lt;&lt;jboss-root&gt;&gt;/module</span> directory.&nbsp; </span></span><br /><ul></ul><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">I placed the three aries-bundles, the h2-bundle and the two slf4j-bundles there.</span><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">For every bundle you have to repeat the following steps:</span><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">If the bundle name is e.g. com.a.b.c.jar create in </span><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Trebuchet MS&quot;,sans-serif;">&lt;&lt;jboss-root&gt;&gt;/module</span> </span>the directory <span style="font-family: &quot;Trebuchet MS&quot;,sans-serif;">com/a/b/main</span> and place the bundle in the main directory. (</span><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">Don't worry about the </span><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Trebuchet MS&quot;,sans-serif;">&lt;&lt;jboss-root&gt;&gt;/module/system</span> -directory.) </span><br /><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">After that, alter the standalone.xml and add a new capability under the OSGi-Subsystem, e.g.:</span><br /><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&lt;subsystem xmlns="urn:jboss:domain:osgi:1.2" activation="eager"&gt;<br />&nbsp;&nbsp; &lt;properties&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&nbsp;&nbsp;&nbsp; ...<br />&nbsp;&nbsp; &lt;/properties&gt;<br />&nbsp;&nbsp; &lt;capabilities&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;capability name="com.a.b.c" startlevel="1"/&gt;<br />&nbsp;&nbsp; &lt;/capabilities&gt;</span><br /><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&lt;/subsystem&gt;</span><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><br /></span><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">Pick whichever startlevel you think is appropriate.</span><br /><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">In fact, you can pick any directory-structure you want to, as long as the path in the standalone.xml is correct. That means you could place the h2-bundle e.g. in </span><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Trebuchet MS&quot;,sans-serif;">&lt;&lt;jboss-root&gt;&gt;/module/org/foo/bar/main </span></span>and JBoss would still be able to find it. Only the last directory has to be named "main".</span><br /><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">After you've done that, take the other bundles an put them into </span><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">&nbsp;</span></span></span></span><br /><span style="font-family: &quot;Trebuchet MS&quot;,sans-serif;">&lt;&lt;jboss-root&gt;&gt;/standalone/deplyoments</span><br /><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">An that's all. These steps should get Activiti running as OSGi-bundle.</span></span><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;">My whole osgi-subsystem configuration looks like this:</span></span><br /><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;">&lt;subsystem xmlns="urn:jboss:domain:osgi:1.2" activation="eager"&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;properties&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="org.osgi.framework.startlevel.beginning"&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/properties&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;capabilities&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;capability name="javax.servlet.api:v25" startlevel="1"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;capability name="javax.transaction.api" startlevel="1"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;capability name="org.osgi.core" startlevel="1"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;capability name="org.osgi.enterprise" startlevel="1"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;capability name="org.slf4j" startlevel="1"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;capability name="org.slf4j.impl" startlevel="1"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;capability name="org.apache.aries.util" startlevel="1"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;capability name="org.apache.aries.proxy" startlevel="1"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;capability name="org.apache.aries.blueprint" startlevel="1"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;capability name="org.h2" startlevel="1"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;capability name="org.apache.felix.log" startlevel="2"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;capability name="org.jboss.osgi.logging" startlevel="2"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;capability name="org.apache.felix.configadmin" startlevel="2"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/capabilities&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/subsystem&gt;&nbsp;</span></span><br /><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">And in my deployments-directory you can find those jars:</span></span></span><br /><ul><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">activiti-bpmn-converter-5.12.1.jar</span></span></span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">activiti-bpmn-model-5.12.1.jar</span></span></span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">activiti-engine-5.12.1.jar</span></span></span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">activiti-osgi-5.12.1.jar</span></span></span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">com.springsource.javax.transaction-1.1.0.jar</span></span></span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">com.springsource.org.apache.commons.lang-2.4.0.jar</span></span></span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">joda-time_2.1.0.jar</span></span></span></li><li><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">org.mybatis.mybatis_3.1.1.jar</span></span></span></li></ul><h4><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">&nbsp;</span></span></span></h4><h4><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">Blueprint activation</span></span></span></h4><h4><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">&nbsp;</span></span></span></h4><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">The Activiti-OSGi bundle contains the basic classes Blueprint needs. But we have to register all those services. That's why we need a Blueprint content.xml.</span></span></span><br /><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">Thanks to Tijs Rademakers we can copy that from <a href="http://code.google.com/p/activitiinaction/" target="_blank">here</a>. It's the example code from/for Activiti in Action.</span></span></span><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">We can also use the book-osgi-app in JBoss. </span></span></span><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">Just run <span style="font-family: &quot;Courier New&quot;,Courier,monospace;">mvn package</span> on the book-engine, book-process and book-task projects.</span></span></span><br /><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">After packaging those three, I had to make a minor change in the MANIFEST.MF and content.xml of the book-engine-project.</span></span></span><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">In the content.xml you have to replace the jdbc-url with whatever fits to your database. </span></span></span><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">In the MANIFEST.MF I had to remove the Import-Package versions for everything related to activiti, because the generated MANIFEST requires version [5.9, 6) and my activiti-bundles don't contain any version information. </span></span></span><br /><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">Now place those three bundles into the deployment-directory and everything should work as expected.</span></span></span><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">To test the deployment, I wrote a simple bundle with an activator to look up the Deployment-Service and see, if a process defnition could be found.&nbsp;</span></span></span><br /><br /><h3><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">Summary&nbsp;</span></span></span></h3><h3><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">&nbsp;</span></span></span></h3><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">Get all the bundles, place some in the modules-directory and some in the deployment-directory, copy some content.xmls and everything should work. Sounds easy, but it took</span></span></span><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">me a while to figure it out ;)</span></span></span><br /><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">So have fun with Activiti, OSGi and JBoss!</span></span></span><br /><span style="font-family: &quot;Helvetica Neue&quot;,Arial,Helvetica,sans-serif;"><span style="font-family: &quot;Courier New&quot;,Courier,monospace;"><span style="font-family: &quot;Helvetica Neue&quot;, Arial, Helvetica, sans-serif;">In case you find any mistake, feel free to point them out.</span></span></span><br /> Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com0tag:blogger.com,1999:blog-2497944031743543766.post-58686105738337434102012-11-17T16:16:00.001+01:002012-11-17T16:16:35.813+01:00How to JUnit-test EnversRecently, me and some fellow students of mine had to do a homework assignment with Spring and Hibernate. One of the requirements was to include versioning for some of the entities.<br />At first we thought about programming something on our own by using AOP and an interface. After suggesting this to our professor he told us, that there might be already a framework for versioning that works with Hibernate.<br />So I found <a href="http://www.jboss.org/envers" target="_blank">Envers</a>.<br />Using Envers isn't very difficult. If you want to know more about Envers, you'll have to take a look at its <a href="http://docs.jboss.org/hibernate/envers/3.6/reference/en-US/html_single/" target="_blank">documentation</a>.<br />My problem was how to write JUnit-Tests for the methods in my DAOs that use the AuditReader class.<br /><br />Before I start explaining my problem and my solution I just want to tell you, that I'm no Spring developer and it was my first project with Spring (my teammates had no experience with Spring, too).<br /><br />When persisting some objects in the tests Envers never wrote any data into its revision tables. I soon found out that the @Transactional annotation at my test-class put every method in one transaction (Spring people will know ;) ). At the end of one test-method the commit was performed and Envers didn't react.<br />So @Transactional had to vanish and the search began.<br /><br />So when I tried to find a solution I found <a href="http://java.dzone.com/articles/how-test-code-uses-envers" target="_blank">this</a> article on Dzone but calling commit() on my session or using the TransactionManager lead to some exceptions.<br /><a href="http://stackoverflow.com/questions/8363815/integration-testing-with-hibernate-envers" target="_blank">This</a> thread on stackoverflow finally was the solution. Unfortunately the second answer doesn't have any rating, so I ignored it and I had to visit that thread three times to recognize it. (please rate it up if you're registered on stackoverflow)<br /><br />The superclass for all my Envers tests looks like this:&nbsp;<a href="https://github.com/flrnb/iaa_hausarbeit/blob/master/src/test/java/de/nak/iaa/ApplicationContextAwareTest.java" target="_blank">ApplicationContextAwareTest</a><br />and in every test it use this method with the TransactionTemplate:<br /><pre><div class="line" id="LC65"><br /><span class="kd">public</span> <span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">T</span> <span class="n">makePersistentInTransaction</span><span class="o">(</span><span class="kd">final</span> <span class="n">T</span> <span class="n">toPersist</span><span class="o">,</span></div><br /><div class="line" id="LC66"><br /> <span class="kd">final</span> <span class="n">GenericDAO</span><span class="o">&lt;</span><span class="n">T</span><span class="o">,</span> <span class="n">Long</span><span class="o">&gt;</span> <span class="n">dao</span><span class="o">)</span> <span class="o">{</span></div><br /><div class="line" id="LC67"><br /> <span class="k">return</span> <span class="n">transactionTemplate</span><span class="o">.</span><span class="na">execute</span><span class="o">(</span><span class="k">new</span> <span class="n">TransactionCallback</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;()</span> <span class="o">{</span></div><br /><div class="line" id="LC68"><br /></div><br /><div class="line" id="LC69"><br /> <span class="nd">@Override</span></div><br /><div class="line" id="LC70"><br /> <span class="kd">public</span> <span class="n">T</span> <span class="nf">doInTransaction</span><span class="o">(</span><span class="n">TransactionStatus</span> <span class="n">status</span><span class="o">)</span> <span class="o">{</span></div><br /><div class="line" id="LC71"><br /> <span class="k">return</span> <span class="n">dao</span><span class="o">.</span><span class="na">makePersistent</span><span class="o">(</span><span class="n">toPersist</span><span class="o">);</span></div><br /><div class="line" id="LC72"><br /> <span class="o">}</span></div><br /><div class="line" id="LC73"><br /> <span class="o">});</span></div><br /><div class="line" id="LC74"><br /><span class="o">};</span></div><br /></pre><br />After every call there is a commit and Envers starts working. And after persisting some objects I can tests my methods, which take care of versioning.<br />Finally everything could be tested and I was happy :D&nbsp; <br /><br />Feel free to browse the GitHub project. It contains the whole homework. And please don't wonder that nearly everything is in German ;)<br /><br />Ronny Bräunlichhttps://plus.google.com/117165668963723139905noreply@blogger.com0