<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[@mgreau :: Blog]]></title><description><![CDATA[Technology enthusiast: Java (EE), Asciidoctor, Docker, Git, Maven and Web.]]></description><link>https://mgreau.com/posts</link><image><url>http://mgreau.com/posts/images/cover-blog-devoxxfr2015.jpg</url><title>@mgreau :: Blog</title><link>https://mgreau.com/posts</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 19 Apr 2017 09:21:23 GMT</lastBuildDate><atom:link href="https://mgreau.com/posts/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[[Talk] Migrate Maven CI jobs to Pipeline as code with Jenkins 2 and Docker]]></title><description><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>This is the talk I gave at DevopsDDay 2016 and Docker Meetup Nantes 2017 about Jenkins 2, Maven, Pipeline as Code and Docker. It was the opportunity to revisit the problems that we have had and the solutions adopted, and some best practices around <strong>Maven builds in Docker containers</strong>, all managed by the <strong>Jenkins Pipeline as Code pattern</strong>:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><span class="icon"><i class="fa fa-youtube"></i></span> <a href="https://youtu.be/MxaQW56tiGE?list=PLAnrr6UexefnE4mJVeTDwGld3nU9VZ3bX">Video Migrate Maven CI jobs to Pipeline as code with Jenkins 2 and Docker @ DevopsDDay</a></p>
</li>
<li>
<p><span class="icon"><i class="fa fa-file"></i></span> <a href="https://mgreau.github.io/jenkins2-pipeline-maven-docker/">Slides Migrate Maven CI jobs to Pipeline as code with Jenkins 2 and Docker</a></p>
</li>
<li>
<p>Source Code</p>
<div class="ulist">
<ul>
<li>
<p><span class="icon"><i class="fa fa-code"></i></span> <a href="https://github.com/mgreau/jenkins2-pipeline-maven-docker/tree/master/demo">Source Code - docker-compose.yml for Jenkins and Sonar @ <strong>Github</strong></a></p>
</li>
<li>
<p><span class="icon"><i class="fa fa-code"></i></span> <a href="https://github.com/mgreau/dockerfiles/tree/master/jenkins2">Source Code - mgreau/jenkins2 Dockerfile @ <strong>Github</strong></a></p>
</li>
<li>
<p><span class="icon"><i class="fa fa-code"></i></span> <a href="https://github.com/mgreau/exo-pipeline-library/tree/master/">Source Code - exo-pipeline-library samples @ <strong>Github</strong></a></p>
</li>
</ul>
</div>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_embedded_video">Embedded Video</h2>
<div class="sectionbody">
<iframe width="560" height="315" src="https://www.youtube.com/embed/MxaQW56tiGE?list=PLAnrr6UexefnE4mJVeTDwGld3nU9VZ3bX" frameborder="0" allowfullscreen></iframe><div style="margin-bottom:5px"> Recorded @ <strong>DevopsDDay 2016</strong> </div>
</div>
</div>
<div class="sect1">
<h2 id="_embedded_slides">Embedded Slides</h2>
<div class="sectionbody">
<iframe src="//mgreau.github.io/jenkins2-pipeline-maven-docker/" width="595" height="485" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"> <strong> <a href="//mgre
au.com/jenkins2-pipeline-maven-docker/" title="Migrate Maven CI jobs to Pipeline as code with Jenkins 2 and Docker" target="_blank">Migrate Maven CI jobs to Pipeline as code with Jenkins 2 and Docker</a> </strong> from <strong><a href="//mgreau.github.io/jenkins2-pipeline-maven-docker/" target="_blank">DevopsDDay 2016 & Docker Meetup 2017</a></strong> </div>
</div>
</div>
<div class="sect1">
<h2 id="_summary_of_the_presentation">Summary of the presentation</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Click, click, click&#8230;&#8203;
The clicks in the Jenkins UI to handle all the Maven jobs haunt you even at night?
You want to manage them with the same process that you use for source code?</p>
</div>
<div class="paragraph">
<p>If I tell you that you can manage all your Maven builds with 3 lines of Groovy in a Jenkinsfile, it’s cool, isn’it?</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-groovy" data-lang="groovy">exoCI {
    dockerImage = ‘exoplatform/ci:jdk8-maven32’
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>With this case study you will understand why and how we have upgraded more than 300 Jenkins “standard Maven jobs” to Pipelines on our eXo Platform CI instance.<br>
This talk is an opportunity to revisit the problems that we have had and the solutions adopted, and <strong>some best practices around Maven builds in Docker containers</strong>, all managed by the <strong>Jenkins Pipeline as Code</strong> pattern.</p>
</div>
</div>
</div>]]></description><link>https://mgreau.com/posts/2017/03/01/maven-ci-jobs-to-pipeline-with-jenkins2-and-docker.html</link><guid isPermaLink="true">https://mgreau.com/posts/2017/03/01/maven-ci-jobs-to-pipeline-with-jenkins2-and-docker.html</guid><category><![CDATA[docker]]></category><category><![CDATA[ maven]]></category><category><![CDATA[ jenkins]]></category><category><![CDATA[ pipeline]]></category><category><![CDATA[ talk]]></category><dc:creator><![CDATA[Maxime Gréau]]></dc:creator><pubDate>Wed, 01 Mar 2017 00:00:00 GMT</pubDate></item><item><title><![CDATA[Convert AsciiDoc to HTML/PDF & publish to GitHub Pages with Travis CI and Asciidoctor Docker containers]]></title><description><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="title">Purpose</div>
<div class="paragraph">
<p>This blog post presents a simple way to automatically convert your AsciiDoc content to HTML and PDF, then publish those files to a public website on each commit.</p>
</div>
<div class="paragraph">
<p>There are many way to do it, maybe you have already used Jekyll or other static site generator like that. The real benefit of this one is that the workflow is done thanks to <a href="#travis_configuration_file"><strong>only one</strong> Travis CI configuration file</a>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p><strong>The High-Level View</strong><br>
You have all your AsciiDoc sources files on one branch, all the generated files on another branch and <strong>just one configuration file</strong>.<br>
All resources explained in this post are listed below:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>AsciiDoc</code> files and <code>.travis.yml</code> configuration file on <a href="https://github.com/mgreau/asciidoc-to-ghpages/tree/master">asciidoc-to-ghpages project <strong>master branch</strong></a></p>
</li>
<li>
<p><code>HTML</code> and <code>PDF</code> files on <a href="https://mgreau.com/asciidoc-to-ghpages/">asciidoc-to-ghpages project <strong>gh-pages branch</strong></a></p>
</li>
<li>
<p>Files published at <a href="https://mgreau.com/asciidoc-to-ghpages/" class="bare">https://mgreau.com/asciidoc-to-ghpages/</a></p>
</li>
</ul>
</div>
</div>
<div id="toc" class="toc">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#_overview">Overview</a></li>
<li><a href="#_prerequisites">Prerequisites</a></li>
<li><a href="#_prepare_your_github_project">Prepare your GitHub project</a>
<ul class="sectlevel2">
<li><a href="#_add_files_to_your_github_project">Add files to your GitHub project</a></li>
<li><a href="#_create_a_token_for_travis_ci">Create a Token for Travis CI</a></li>
</ul>
</li>
<li><a href="#_travis_ci_configuration">Travis CI Configuration</a>
<ul class="sectlevel2">
<li><a href="#_connect_travis_ci_to_your_github_project">Connect Travis CI to your GitHub project</a></li>
<li><a href="#__travis_yml_travis_ci_configuration_and_scripts">.travis.yml: Travis CI Configuration and Scripts</a></li>
<li><a href="#_travis_build_logs">Travis Build Logs</a></li>
</ul>
</li>
<li><a href="#_published_files_html_pdf">Published files (HTML &amp; PDF)</a></li>
</ul>
</div>
</div>
<div class="sect1">
<h2 id="_overview">Overview</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The following diagram gives you a quick <strong>overview of all interactions</strong> between <a href="https://github.com">GitHub</a>, <a href="https://github.com">Travis CI</a>, <a href="https://github.com">Docker</a> and <a href="https://github.com">Asciidoctor</a>.<br>
Travis CI makes it possible because it allows you to run any Docker container. This isolates your build from the machine running on Travis CI and allows you to use an Asciidoctor environment that has been prepared for you.</p>
</div>
<div id="asciidoc_ghpages_travis_docker" class="imageblock">
<div class="content">
<a class="image" href="http://mgreau.com/posts/images/cover-asciidoc-ghpages.svg"><img src="http://mgreau.com/posts/images/cover-asciidoc-ghpages.svg" alt="AsciiDoc to Github Pages with Travis and Docker Asciidoctor" width="950"></a>
</div>
<div class="title">Figure 1. Convert your AsciiDoc content to HTML &amp; PDF, then publish those files to GitHub Pages courtesy of Travis CI running Asciidoctor via the provided Docker container(s).</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_prerequisites">Prerequisites</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This post assumes that:</p>
</div>
<div class="ulist checklist">
<ul class="checklist">
<li>
<p><i class="fa fa-check-square-o"></i> you know <strong>AsciiDoc</strong> and  <a href="https://asciidoctor.org">Asciidoctor</a></p>
</li>
<li>
<p><i class="fa fa-check-square-o"></i> you have a <strong>GitHub account</strong> and a GitHub project configured to publish content from the <strong>gh-pages</strong> branch</p>
<div class="ulist">
<ul>
<li>
<p>if it&#8217;s not the case you can read <a href="https://help.github.com/articles/creating-project-pages-manually/">the GitHub tutorials about GitHub Pages</a></p>
</li>
</ul>
</div>
</li>
<li>
<p><i class="fa fa-check-square-o"></i> you have a <strong>Travis account</strong> linked to your GitHub account</p>
<div class="ulist">
<ul>
<li>
<p>if it&#8217;s not the case, you can read the <a href="https://docs.travis-ci.com/user/for-beginners">Travis CI Guide for beginners</a></p>
</li>
</ul>
</div>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_prepare_your_github_project">Prepare your GitHub project</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_add_files_to_your_github_project">Add files to your GitHub project</h3>
<div class="paragraph">
<p>The first step is to push some files to your master branch. The following GitHub project gives you a sample AsciiDoc content using images and include directive.</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://github.com/mgreau/asciidoc-to-ghpages/tree/master">AsciiDoc to GitHub Pages demo project (master branch)</a></p>
</li>
</ul>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
All samples come from this <a href="https://github.com/opendevise/asciidoc-samples">OpenDevise GitHub repository</a>, thanks to Dan Allen.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>All files available on this demo project are explained below:</p>
</div>
<div class="exampleblock">
<div class="title">Example 1. Files listed on <strong>master branch</strong></div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">+ images
   |- tiger.png     <i class="conum" data-value="1"></i><b>(1)</b>
+ includes
   |- include.adoc  <i class="conum" data-value="2"></i><b>(2)</b>
.gitignore
.travis.yml    <i class="conum" data-value="3"></i><b>(3)</b>
demo.adoc      <i class="conum" data-value="4"></i><b>(4)</b>
LICENSE
README.adoc    <i class="conum" data-value="5"></i><b>(5)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Image used in AsciiDoc content</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>AsciiDoc content included in the <code>demo.adoc</code> content</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Travis configuration and scripts to build and publish the project, this file is explained in the <a href="#travis_configuration_file">Travis part.</a></td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Sample AsciiDoc file to convert</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Documentation for the GitHub project, then converted to index.html file for the website</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_create_a_token_for_travis_ci">Create a Token for Travis CI</h3>
<div class="paragraph">
<p>Travis CI will push files on a dedicated branch of your GitHub project, so you need to <strong>create a token</strong> to <strong>allow Travis CI</strong> to do it.<br>
As you can see on the following screenshot, you just need to access to your <code>GithHub Personal Settings</code> page and click on the <code>Generate token</code> button, then check the <code>public_repo</code> scope:</p>
</div>
<div id="asciidoc_ghpages_github_token" class="imageblock post-cover-image">
<div class="content">
<img src="/posts/images/asciidoc-ghpages-github-token.png" alt="GitHub Token Configuration" width="950">
</div>
<div class="title">Figure 2. GitHub Configuration: Create a token used by Travis CI</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_travis_ci_configuration">Travis CI Configuration</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_connect_travis_ci_to_your_github_project">Connect Travis CI to your GitHub project</h3>
<div class="paragraph">
<p>Once your AsciiDoc project is available on the master branch, you need to access to your <code>Travis CI Dashboard</code> and do some quick configuration so that Travis CI can build your project:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>First, on your <strong>Travis Profile page</strong>, you need to <code>Flick the repository switch on</code> for your GitHub project:</p>
</li>
</ol>
</div>
<div id="asciidoc_ghpages_travis_activate" class="imageblock post-cover-image">
<div class="content">
<img src="/posts/images/asciidoc-ghpages-travis-activate.png" alt="Travis Configuration" width="950">
</div>
<div class="title">Figure 3. Travis CI Configuration: Activate Travis CI on the project</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
You can&#8217;t see your project on the list? Click on the <code>Sync account</code> button (top right) and it should be ok.
</td>
</tr>
</table>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Then, as shown below, go to the <code>Project Settings tabs</code> and configure it:</p>
<div class="olist loweralpha">
<ol class="loweralpha" type="a">
<li>
<p>Check some options on <code>General Settings</code></p>
<div class="olist lowerroman">
<ol class="lowerroman" type="i">
<li>
<p>check the <code>Build only if .travis.yml is present</code> option</p>
</li>
<li>
<p>check the <code>Build pushes</code> option</p>
</li>
</ol>
</div>
</li>
<li>
<p>Create some <code>Environment Variables</code> that will be used in <a href="#travis_configuration_file">.travis.yml</a> file:</p>
<div class="olist lowerroman">
<ol class="lowerroman" type="i">
<li>
<p><code>GH_USER_NAME</code> : your GitHub username</p>
</li>
<li>
<p><code>GH_USER_EMAIL</code> : your GitHub account email</p>
</li>
<li>
<p><code>GH_TOKEN</code>: the token created on <a href="#create-a-token-for-travis-ci">previous step</a></p>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
Uncheck the <code>Display value in build log</code> option
</td>
</tr>
</table>
</div>
</li>
<li>
<p><code>GH_REF</code> : the URL to your github project</p>
</li>
</ol>
</div>
</li>
</ol>
</div>
</li>
</ol>
</div>
<div id="asciidoc_ghpages_travis_config" class="imageblock post-cover-image">
<div class="content">
<img src="/posts/images/asciidoc-ghpages-travis-env.png" alt="Travis Configuration" width="950">
</div>
<div class="title">Figure 4. Travis CI Configuration: Project Settings</div>
</div>
</div>
<div class="sect2">
<h3 id="__travis_yml_travis_ci_configuration_and_scripts">.travis.yml: Travis CI Configuration and Scripts</h3>
<div class="paragraph">
<p>The <code>.travis.yml</code> file is the most important file here ;)<br>
Indeed, in this file, you tell to Travis CI how to process your AsciiDoc files and where to publish the generated files.<br>
Those scripts will be executed after each update on master branch.</p>
</div>
<div id="travis_configuration_file" class="listingblock">
<div class="title">file .travis.yml</div>
<div class="content">
<pre class="highlight"><code class="language-yaml" data-lang="yaml">sudo: required

services:
  - docker                  <i class="conum" data-value="1"></i><b>(1)</b>

before_install:            <i class="conum" data-value="2"></i><b>(2)</b>
  - mkdir -p output
  - docker pull asciidoctor/docker-asciidoctor

script:
  - docker run -v $TRAVIS_BUILD_DIR:/documents/ --name asciidoc-to-html asciidoctor/docker-asciidoctor asciidoctor -D /documents/output *.adoc      <i class="conum" data-value="3"></i><b>(3)</b>
  - docker run -v $TRAVIS_BUILD_DIR:/documents/ --name asciidoc-to-pdf asciidoctor/docker-asciidoctor asciidoctor-pdf -D /documents/output *.adoc    <i class="conum" data-value="4"></i><b>(4)</b>

after_error: <i class="conum" data-value="5"></i><b>(5)</b>
  - docker logs asciidoc-to-html
  - docker logs asciidoc-to-pdf

after_failure:
  - docker logs asciidoc-to-html
  - docker logs asciidoc-to-pdf

after_success:      <i class="conum" data-value="6"></i><b>(6)</b>
  - cd output ; mv README.html index.html ; cp -R ../images images
  - git init
  - git config user.name "${GH_USER_NAME}"
  - git config user.email "{GH_USER_EMAIL}"
  - git add . ; git commit -m "Deploy to GitHub Pages"
  - git push --force --quiet "https://${GH_TOKEN}@${GH_REF}" master:gh-pages &gt; /dev/null 2&gt;&amp;1</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Tell Travis CI that you want to use <strong>Docker</strong></td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Create the needed <strong>output directory</strong> and download the official <strong>Asciidoctor Docker</strong> image from DockerHub</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Convert AsciiDoc files to <strong>HTML</strong> to the <code>/documents/output/</code> path with a Docker container</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Convert AsciiDoc files to <strong>PDF</strong> to the <code>/documents/output/</code> path with another Docker container</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Display logs from containers if a problem occured</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>Initialize a git project in the `output/ sub-directory in order to <strong>push the generated files and images</strong> to the <strong>gh-pages branch</strong></td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_travis_build_logs">Travis Build Logs</h3>
<div class="paragraph">
<p>Travis CI gives you access to each build logs.<br>
You can see in realtime, what is going on when AsciiDoc files are <strong>processed by Asciidoctor</strong> on each Docker container (<em>one for HTML output and one for PDF output here</em>)</p>
</div>
<div id="asciidoc_ghpages_travis_build" class="imageblock post-cover-image">
<div class="content">
<img src="/posts/images/asciidoc-ghpages-travis-build.png" alt="Travis CI Build" width="950">
</div>
<div class="title">Figure 5. Travis CI Build</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_published_files_html_pdf">Published files (HTML &amp; PDF)</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Finally, when the Travis CI Build has succeeded, you can view the generated files at:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://mgreau.com/asciidoc-to-ghpages/" class="bare">https://mgreau.com/asciidoc-to-ghpages/</a></p>
<div class="ulist">
<ul>
<li>
<p><a href="https://mgreau.com/asciidoc-to-ghpages//demo.html">Demo HTML</a></p>
</li>
<li>
<p><a href="https://mgreau.com/asciidoc-to-ghpages//demo.pdf">Demo PDF</a></p>
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="exampleblock">
<div class="title">Example 2. Files listed on <strong>gh-pages branch</strong></div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">+ images            <i class="conum" data-value="1"></i><b>(1)</b>
   |- tiger.png
demo.html         <i class="conum" data-value="2"></i><b>(2)</b>
demo.pdf          <i class="conum" data-value="3"></i><b>(3)</b>
index.html    <i class="conum" data-value="4"></i><b>(4)</b>
README.pdf</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The images folder is copied so that images are published to GitHub Pages Website</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>The HTML output file</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>The PDF output file</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>The <code>README.html</code> is renamed to <code>index.html</code> in order to have a homepage file for the Website</td>
</tr>
</table>
</div>
</div>
</div>
<div class="paragraph">
<p>You can see below a screenshot with both <a href="https://mgreau.com/asciidoc-to-ghpages//demo.html">demo.html</a> and <a href="https://mgreau.com/asciidoc-to-ghpages//demo.pdf">demo.pdf</a> files.</p>
</div>
<div id="asciidoc_ghpages_files_published" class="imageblock post-cover-image">
<div class="content">
<a class="image" href="/posts/images/asciidoc-ghpages-final.png"><img src="/posts/images/asciidoc-ghpages-final.png" alt="AsciiDoc to HTML and PDF published files" width="950"></a>
</div>
<div class="title">Figure 6. HTML and PDF files published on GitHub Pages converted from the same AsciiDoc content by Asciidoctor</div>
</div>
<div class="paragraph">
<p>I hope this post was useful and it helped you understand how to combine Travis CI and Docker to automatically convert content written in <strong>AsciiDoc</strong> and publish the output to GitHub Pages on each commit.<br>
There are a lot of other ways to do it, I’d love to have your feedbacks about it, so feel free to add a comment.</p>
</div>
</div>
</div>]]></description><link>https://mgreau.com/posts/2016/03/28/asciidoc-to-gh-pages-with-travis-ci-docker-asciidoctor.html</link><guid isPermaLink="true">https://mgreau.com/posts/2016/03/28/asciidoc-to-gh-pages-with-travis-ci-docker-asciidoctor.html</guid><category><![CDATA[asciidoc]]></category><category><![CDATA[ Asciidoctor]]></category><category><![CDATA[ travis]]></category><category><![CDATA[ docker]]></category><category><![CDATA[ gh-pages]]></category><dc:creator><![CDATA[Maxime Gréau]]></dc:creator><pubDate>Mon, 28 Mar 2016 00:00:00 GMT</pubDate></item><item><title><![CDATA[[Talk] Improve your Java Environment with Docker @ HanoiJUG 2015]]></title><description><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Our Session with Thomas Delhoménie about how to <strong>Improve your Java Development Environment with Docker</strong> at <a href="http://www.meetup.com/fr-FR/Hanoi-Java-User-Group/events/225962831/?eventId=225962831"><strong>Hanoi JUG 2015</strong></a> with a <strong>Java EE 7</strong> / <strong>Angular</strong> / WildFly / Redis / MySQL Application with <strong>Docker containers</strong>:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><span class="icon"><i class="fa fa-file"></i></span> <a href="http://fr.slideshare.net/HanoiJUG/improve-your-java-environment-with-docker">Slides Improve your Java Development Environment with Docker @ <strong>Slideshare</strong></a></p>
</li>
<li>
<p><span class="icon"><i class="fa fa-code"></i></span> <a href="https://github.com/mgreau/docker4dev-tennistour-app">Source Code @ <strong>Github</strong></a></p>
</li>
</ul>
</div>
<iframe src="//fr.slideshare.net/slideshow/embed_code/key/jN42dCgFzSF9Pp" width="595" height="485" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"> <strong> <a href="//fr.slideshare.net/HanoiJUG/improve-your-java-environment-with-docker" title="Improve your Java Environment with Docker" target="_blank">Improve your Java Environment with Docker</a> </strong> from <strong><a href="//fr.slideshare.net/HanoiJUG" target="_blank">HanoiJUG</a></strong> </div>
</div>
</div>
<div class="sect1">
<h2 id="_description">Description:</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_dockerize_your_java_development_environment_with_machine_compose">Dockerize your Java Development Environment with Machine &amp; Compose</h3>
<div class="paragraph">
<p>Developers, <strong>Docker Compose</strong> is the key to be efficient with your development environment!</p>
</div>
<div class="paragraph">
<p>Whatever the project and the technologies behind it, all developers should be able to work on it quickly <strong>without losing time to setup their development environment</strong>.</p>
</div>
<div class="paragraph">
<p>Today, we really think that Docker, with Machine &amp; Compose, have changed the way to work on developer side.</p>
</div>
<div class="paragraph">
<p>And now, as a developer, you are able to develop quickly with an environment closest to the production environment.</p>
</div>
<div class="paragraph">
<p>We will show you how you can develop, build, debug and run your Java, Web, multi-databases applications <strong>easily thanks to Docker</strong>.</p>
</div>
</div>
</div>
</div>]]></description><link>https://mgreau.com/posts/2015/11/05/improve-java-dev-environment-with-docker.html</link><guid isPermaLink="true">https://mgreau.com/posts/2015/11/05/improve-java-dev-environment-with-docker.html</guid><category><![CDATA[docker]]></category><category><![CDATA[ docker-compose]]></category><category><![CDATA[ docker4dev]]></category><category><![CDATA[ Java]]></category><category><![CDATA[ Angular]]></category><category><![CDATA[ HanoiJUG]]></category><category><![CDATA[ JUG]]></category><category><![CDATA[ talk]]></category><dc:creator><![CDATA[Maxime Gréau]]></dc:creator><pubDate>Thu, 05 Nov 2015 00:00:00 GMT</pubDate></item><item><title><![CDATA[#docker4Dev: Copy Docker images from one docker machine to another]]></title><description><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="title">Overview</div>
<div class="paragraph">
<p>In this post, you will see how to <strong>copy images from one docker machine to another by using docker-machine scp command</strong> available since docker-machine 0.3.0.
It may be useful if you have <strong>no Internet access neither local docker registry installed</strong>.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>if you are already familiar with docker-machine, you can jump ahead and start <a href="#_copy_image_from_machine_to_machine">reading how to copy images</a>.</p>
</li>
<li>
<p>if you want to see it in action right know, you can <a href="#_see_it_in_action_terminal_session_demo">watch the terminal session video</a> recorded with <a href="https://asciinema.org/">Asciinema</a> (01:25, speed x3)</p>
</li>
</ul>
</div>
<div class="paragraph">
<div class="title">About this post</div>
<p>Language <strong><em>EN</em></strong> /  Timereading <strong><em>10 mn</em></strong></p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Today I really think that Docker is not only a revolution for Ops and DevOps teams, but also on the Development environment side and that&#8217;s just a beginning.</p>
</div>
<div class="paragraph">
<p>That&#8217;s why I have <strong>a lot of docker machines created on my laptop</strong> in order to organize my several development environments. But the fact is that you frequently want to use an image on a machine that is already present on another existing docker machine. And if you are offline or if you Internet connection is slow, <strong>you can&#8217;t execute a 'docker pull' command</strong>, so <strong>how to deal with that?</strong></p>
</div>
<div id="docker_machine_scp" class="imageblock">
<div class="content">
<img src="http://mgreau.com/posts/images/cover-docker-machine-scp.png" alt="Docker Machine scp" width="950">
</div>
<div class="title">Figure 1. Copy Docker images from machine to machine with scp</div>
</div>
</div>
<div id="toc" class="toc">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#_install_docker_machine">Install Docker Machine</a>
<ul class="sectlevel2">
<li><a href="#_installation_via_docker_toolbox">Installation via Docker Toolbox</a></li>
<li><a href="#_installation_via_command_line">Installation via Command line</a></li>
</ul>
</li>
<li><a href="#_copy_image_from_machine_to_machine">Copy image from machine to machine</a>
<ul class="sectlevel2">
<li><a href="#_commands_explained">Commands explained</a></li>
<li><a href="#_see_it_in_action_terminal_session_demo">See it in action: Terminal Session demo</a></li>
</ul>
</li>
<li><a href="#_export_an_image_from_a_machine_and_load_it_to_another_machine_thought_your_host">Export an image from a machine and load it to another machine thought your host</a></li>
</ul>
</div>
</div>
<div class="sect1">
<h2 id="_install_docker_machine">Install Docker Machine</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_installation_via_docker_toolbox">Installation via Docker Toolbox</h3>
<div class="paragraph">
<p>Two weeks ago, Docker announced a <strong>new installer</strong> for Mac OS X and Windows called <strong>Toolbox</strong>.</p>
</div>
<div class="quoteblock">
<blockquote>
Toolbox installs everything you need to get Docker running in development: the Docker client, Compose (Mac only), Kitematic, Machine, and VirtualBox.
</blockquote>
</div>
<div class="paragraph">
<p>Even if, under the hood, Machine still uses <strong>Boot2Docker</strong>, now containers are managed by <strong>Machine</strong>.</p>
</div>
<div class="paragraph">
<p>You can find all informations about <strong>Toolbox</strong> at the following URLs:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>ToolBox blog post: <a href="http://blog.docker.com/2015/08/docker-toolbox/">ANNOUNCING DOCKER TOOLBOX</a></p>
</li>
<li>
<p><a href="https://www.docker.com/toolbox">ToolBox install packages</a></p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_installation_via_command_line">Installation via Command line</h3>
<div class="paragraph">
<p>If you still want to install Docker machine without Toolbox, you can execute the following commands:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">$ curl -L https://github.com/docker/machine/releases/download/v0.4.1/docker-machine_linux-amd64 &gt; /usr/local/bin/docker-machine  <i class="conum" data-value="1"></i><b>(1)</b>
$ chmod +x /usr/local/bin/docker-machine <i class="conum" data-value="2"></i><b>(2)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Download the latest docker-machine binary <strong>according to your target platform</strong></td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Apply executable permissions to the binary</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Then whatever the choice you made for installing it, you should be able to execute the <strong>docker-machine</strong> help command to see the <strong>scp command</strong> on the list:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">$ docker-machine help
...
Version: 0.4.1 (e2c88d6)  <i class="conum" data-value="1"></i><b>(1)</b>
...
Commands:
  active		Print which machine is active
  ...
  scp			Copy files between machines  <i class="conum" data-value="2"></i><b>(2)</b>
  ...
  help, h		Shows a list of commands or help for one command</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Docker Machine 0.4.1 released on August, 14, 2015</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>The <strong>scp</strong> command is available since <a href="https://blog.docker.com/2015/06/docker-machine-0-3-0-deep-dive/">docker-machine 0.3.0</a></td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_copy_image_from_machine_to_machine">Copy image from machine to machine</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Unfortunately, for now it is not possible to do it with one command.
Indeed, the machine scp command is used to copy the export image file between machines, so, before that, the docker machine ssh command is used to manage file on the source machine and at the end on the target machine.</p>
</div>
<div class="paragraph">
<p>The docker machine scp usage is based on the machine names, and is similar to the default scp syntax.</p>
</div>
<div class="sect2">
<h3 id="_commands_explained">Commands explained</h3>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">$ docker-machine ls --filter=state=Running  <i class="conum" data-value="1"></i><b>(1)</b>
NAME                ACTIVE   DRIVER       STATE     URL                         SWARM
default                      virtualbox   Running   tcp://192.168.99.102:2376       <i class="conum" data-value="2"></i><b>(2)</b>
devnation-2015      *        virtualbox   Running   tcp://192.168.99.104:2376       <i class="conum" data-value="3"></i><b>(3)</b>
docker4dev-ee7-js            virtualbox   Running   tcp://192.168.99.105:2376       <i class="conum" data-value="4"></i><b>(4)</b>


$ docker-machine ssh devnation-2015 docker images | grep node  <i class="conum" data-value="5"></i><b>(5)</b>
node                             latest              20a32f7a591c        8 weeks ago         711.5 MB
node                             0.12.4              f9ba67676f8f        9 weeks ago         711.8 MB

$ docker-machine ssh docker4dev-ee7-js docker images | grep node  <i class="conum" data-value="6"></i><b>(6)</b>

$ docker-machine ssh devnation-2015 "docker save -o /tmp/node.tar node:0.12.4" <i class="conum" data-value="7"></i><b>(7)</b>
$ docker-machine scp devnation-2015:/tmp/node.tar docker4dev-ee7-js:/tmp/node.tar <i class="conum" data-value="8"></i><b>(8)</b>
$ docker-machine ssh docker4dev-ee7-js "docker load -i /tmp/node.tar"  <i class="conum" data-value="9"></i><b>(9)</b>
$ docker-machine ssh docker4dev-ee7-js docker images | grep node       <i class="conum" data-value="10"></i><b>(10)</b>
node                0.12.4              f9ba67676f8f        9 weeks ago         711.8 MB</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>display all running machines</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td><strong>default machine</strong> created when you launch the terminal shortcut provided by Toolbox</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>the source machine containing the <strong>node:0.12.4 image to copy</strong></td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>the <strong>target</strong> machine</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>display all <strong>images related to node</strong> on the <strong>source machine</strong></td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>check that there is <strong>no image</strong> related to node on the <strong>target machine</strong> for now</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>export the node image to a TAR file on the source machine</td>
</tr>
<tr>
<td><i class="conum" data-value="8"></i><b>8</b></td>
<td><strong>copy the TAR file</strong> from the source machine to the target machine <strong>via scp command</strong></td>
</tr>
<tr>
<td><i class="conum" data-value="9"></i><b>9</b></td>
<td><strong>load the node image</strong> to the docker local registry of the <strong>target machine</strong></td>
</tr>
<tr>
<td><i class="conum" data-value="10"></i><b>10</b></td>
<td>check that the node image is now available on the target machine</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_see_it_in_action_terminal_session_demo">See it in action: Terminal Session demo</h3>
<div class="paragraph">
<p>All commands described above have been executed on my laptop and thanks to <a href="https://asciinema.org">Asciinema</a>, they have been recorded too:</p>
</div>
<script type="text/javascript" src="https://asciinema.org/a/25473.js" id="asciicast-25473" async data-speed="3"></script>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_export_an_image_from_a_machine_and_load_it_to_another_machine_thought_your_host">Export an image from a machine and load it to another machine thought your host</h2>
<div class="sectionbody">
<div class="paragraph">
<p>An other way to do the same thing, without using the scp command, is to:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><strong>export directly the TAR file to your host</strong> with the <strong>docker client</strong> connected to the <strong>source</strong> machine</p>
</li>
<li>
<p>and then <strong>load this file</strong> to the target machine with the <strong>docker client</strong> connected to the target machine</p>
</li>
</ol>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">$ docker-machine ls  <i class="conum" data-value="1"></i><b>(1)</b>

NAME                           ACTIVE   DRIVER       STATE     URL                         SWARM
default                        *        virtualbox   Running   tcp://192.168.99.102:2376
devnation-2015                          virtualbox   Running   tcp://192.168.99.104:2376


$ eval $(docker-machine env devnation-2015)  <i class="conum" data-value="2"></i><b>(2)</b>
$ docker images | grep alpine                <i class="conum" data-value="3"></i><b>(3)</b>
alpine                           3.1                 fa60145ca189        11 weeks ago        5.033 MB
alpine                           3.2                 8697b6cc1f48        11 weeks ago        5.242 MB


$ docker save  -o /tmp/alpine-3.2.tar alpine:3.2  <i class="conum" data-value="4"></i><b>(4)</b>
$ ls /tmp/alpine*
/tmp/alpine-3.2.tar

$ docker-machine create -d virtualbox docker4dev-ee7-js  <i class="conum" data-value="5"></i><b>(5)</b>
$ eval $(docker-machine env docker4dev-ee7-js)           <i class="conum" data-value="6"></i><b>(6)</b>
$ docker images                                        <i class="conum" data-value="7"></i><b>(7)</b>
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
$ docker load -i /tmp/alpine-3.2.tar                   <i class="conum" data-value="8"></i><b>(8)</b>
$ docker-images                                        <i class="conum" data-value="9"></i><b>(9)</b>
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
alpine              3.2                 8697b6cc1f48        11 weeks ago        5.242 MB</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>display all machines</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td><strong>point the docker client</strong> to the source machine</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>display all images related to alpine on source machine</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td><strong>export the alpine:3.2 image</strong> from the source machine to the host</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>create a new machine with the virtualbox driver</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td><strong>point the docker client</strong> to the target machine</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>check that the target machine does not contain image</td>
</tr>
<tr>
<td><i class="conum" data-value="8"></i><b>8</b></td>
<td><strong>load the alpine:3.2</strong> image from the host to the docker registry of the target machine</td>
</tr>
<tr>
<td><i class="conum" data-value="9"></i><b>9</b></td>
<td>check that the alpine:3.2 is now available on the target machine</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>How do you do this? Do you use a local docker registry reachable by all machines?<br>
I&#8217;d love to have your advices.</p>
</div>
</div>
</div>]]></description><link>https://mgreau.com/posts/2015/08/26/copy-docker-images-from-one-docker-machine-to-another.html</link><guid isPermaLink="true">https://mgreau.com/posts/2015/08/26/copy-docker-images-from-one-docker-machine-to-another.html</guid><category><![CDATA[docker4Dev]]></category><category><![CDATA[ docker-machine]]></category><category><![CDATA[ docker]]></category><dc:creator><![CDATA[Maxime Gréau]]></dc:creator><pubDate>Wed, 26 Aug 2015 00:00:00 GMT</pubDate></item><item><title><![CDATA[[Talk] AsciiDoc : Create and Publish everywhere from anywhere]]></title><description><![CDATA[<div class="paragraph">
<p>My slides from DevNation 2015 at Boston in June.</p>
</div>
<iframe class="frame" width="100%" height="600"  src="http://mgreau.com/slides/devnation2015/dzslides/shells/embedder.html#http://mgreau.com/slides/devnation2015/slides.html"/>]]></description><link>https://mgreau.com/posts/2015/06/22/asciidoc-create-and-publish-everywhere-from-anywhere.html</link><guid isPermaLink="true">https://mgreau.com/posts/2015/06/22/asciidoc-create-and-publish-everywhere-from-anywhere.html</guid><category><![CDATA[Asciidoctor]]></category><category><![CDATA[ AsciiDoc]]></category><category><![CDATA[ DevNation]]></category><category><![CDATA[ DevNation2015]]></category><category><![CDATA[ talk]]></category><dc:creator><![CDATA[Maxime Gréau]]></dc:creator><pubDate>Mon, 22 Jun 2015 00:00:00 GMT</pubDate></item><item><title><![CDATA[Agenda Asciidoctor @ DevoxxFR et t-shirts à gagner]]></title><description><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Dans quelques heures c&#8217;est le début de <strong>Devoxx France 2015</strong> !<br>
Alors que toute l'équipe organisatrice est encore au travail (déjà bravo à eux d&#8217;ailleurs), il est temps de préparer votre programme personnalisé pour ces 3 jours de geeks.</p>
</div>
</div>
<div id="toc" class="toc">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#_asciidoctor_the_big_boss_is_here">Asciidoctor : the big boss is here !</a></li>
<li><a href="#_agenda">Agenda</a>
<ul class="sectlevel2">
<li><a href="#_mercredi_08_avril_2015">Mercredi 08 Avril 2015</a>
<ul class="sectlevel3">
<li><a href="#_tools_in_action_18h40_19h10">Tools In Action @ 18h40 - 19h10</a></li>
</ul>
</li>
<li><a href="#_jeudi_09_avril_2015">Jeudi 09 Avril 2015</a>
<ul class="sectlevel3">
<li><a href="#_keynote_10h30">Keynote @ 10h30</a></li>
<li><a href="#_bof_asciidoctor">BOF Asciidoctor</a></li>
</ul>
</li>
<li><a href="#_vendredi_10_aril_2015">Vendredi 10 Aril 2015</a></li>
<li><a href="#_conf_rence_14h05_14h55">Conférence @ 14h05 - 14h55</a></li>
</ul>
</li>
<li><a href="#_des_t_shirts_gagner">Des T-Shirts à gagner !</a></li>
</ul>
</div>
</div>
<div class="sect1">
<h2 id="_asciidoctor_the_big_boss_is_here">Asciidoctor : the big boss is here !</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Dans ce billet, je vous propose une synthèse des événements DevoxxFR relatifs à Asciidoctor. Comme vous le savez, <a href="https://twitter.com/mojavelinux">Dan Allen</a> le <strong>lead du projet Asciidoctor</strong> est présent cette année, c&#8217;est donc le bon moment pour apprendre du guru d&#8217;<a href="http://asciidoctor.org">Asciidoctor</a> :). Vous pourrez rencontrer également <a href="https://twitter.com/carbonfray">Sarah White</a>, <a href="https://twitter.com/g_scheibel">Guillaume Scheibel</a> et moi-même.</p>
</div>
<div class="paragraph">
<p>Si vous ne nous reconnaissez pas, nous aurons sûrement des <strong>t-shirts Asciidoctor</strong> sur nous :)</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_agenda">Agenda</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_mercredi_08_avril_2015">Mercredi 08 Avril 2015</h3>
<div class="sect3">
<h4 id="_tools_in_action_18h40_19h10">Tools In Action @ 18h40 - 19h10</h4>
<div class="paragraph">
<p><strong><em>Et si on arrêtait de se prendre la tête avec la documentation</em></strong></p>
</div>
<div class="imageblock" style="text-align: left;float: left">
<div class="content">
<img src="https://pbs.twimg.com/profile_images/378800000758259411/ad7fb6994b8b07d8e17e408e167bb4d0_bigger.jpeg" alt="Guillaume" width="50">
</div>
</div>
<div class="paragraph">
<p><strong>Guillaume Sheibel</strong><br>
@gscheibel<br></p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="title">Et si on arrêtait de se prendre la tête avec la documentation</div>
<div class="ulist">
<ul>
<li>
<p>Salle Neuilly 253 Lab</p>
</li>
<li>
<p>18h40 - 19h10</p>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_jeudi_09_avril_2015">Jeudi 09 Avril 2015</h3>
<div class="sect3">
<h4 id="_keynote_10h30">Keynote @ 10h30</h4>
<div class="paragraph">
<p><strong><em>Reading and Writing in 20 Years</em></strong></p>
</div>
<div class="imageblock" style="text-align: left;float: left">
<div class="content">
<img src="https://pbs.twimg.com/profile_images/2044454472/headshot-speaking-20120403_bigger.jpg" alt="Dan" width="50">
</div>
</div>
<div class="paragraph">
<p><strong>Dan Allen</strong><br>
@mojavelinux<br></p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="title">Reading and Writing in 20 Years</div>
<div class="ulist">
<ul>
<li>
<p>Amphi Bleu</p>
</li>
<li>
<p>10h30 - 10h50</p>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_bof_asciidoctor">BOF Asciidoctor</h4>
<div class="imageblock" style="text-align: left;float: left">
<div class="content">
<img src="https://pbs.twimg.com/profile_images/2044454472/headshot-speaking-20120403_bigger.jpg" alt="Dan" width="50">
</div>
</div>
<div class="paragraph">
<p><strong>Dan Allen</strong><br>
@mojavelinux<br></p>
</div>
<div class="imageblock" style="text-align: left;float: left">
<div class="content">
<img src="https://pbs.twimg.com/profile_images/378800000758259411/ad7fb6994b8b07d8e17e408e167bb4d0_bigger.jpeg" alt="Guillaume" width="50">
</div>
</div>
<div class="paragraph">
<p><strong>Guillaume Sheibel</strong><br>
@gscheibel<br></p>
</div>
<div class="imageblock" style="text-align: left;float: left">
<div class="content">
<img src="https://pbs.twimg.com/profile_images/588747726910857216/b0nJW3an_bigger.jpg" alt="Maxime" width="50">
</div>
</div>
<div class="paragraph">
<p><strong>Maxime Gréau</strong><br>
@mgreau<br></p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_vendredi_10_aril_2015">Vendredi 10 Aril 2015</h3>

</div>
<div class="sect2">
<h3 id="_conf_rence_14h05_14h55">Conférence @ 14h05 - 14h55</h3>
<div class="paragraph">
<p><strong><em>Write in AsciiDoc, Publish Everywhere!</em></strong></p>
</div>
<div class="imageblock">
<div class="content">
<img src="https://pbs.twimg.com/profile_images/2044454472/headshot-speaking-20120403_bigger.jpg" alt="Dan">
</div>
</div>
<div class="paragraph">
<p><strong>Dan Allen</strong><br>
@mojavelinux<br></p>
</div>
<div class="imageblock">
<div class="content">
<img src="https://pbs.twimg.com/profile_images/588747726910857216/b0nJW3an_bigger.jpg" alt="Maxime">
</div>
</div>
<div class="paragraph">
<p><strong>Maxime Gréau</strong><br>
@mgreau<br></p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="title">Write in AsciiDoc, Publish Everywhere!</div>
<div class="ulist">
<ul>
<li>
<p>Salle Neuilly 252 AB</p>
</li>
<li>
<p>14h05 - 14h55</p>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_des_t_shirts_gagner">Des T-Shirts à gagner !</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Nous aurons des <strong>beaux t-shirts à manches longues à gagner</strong> pendant ces 3 jours à Devoxx France donc surtout ne loupez pas les événements <strong>Asciidoctor</strong> :)</p>
</div>
<div id="docker_arquillian_asciidoctor" class="imageblock">
<div class="content">
<img src="https://pbs.twimg.com/media/CBmiX7sWYAAyo74.jpg:large" alt="T-shirts Asciidoctor @ Devoxx France" width="400">
</div>
<div class="title">Figure 1. T-shirts Asciidoctor @ Devoxx France (Edition limitée)</div>
</div>
<div class="paragraph">
<p>Pour ma part, c&#8217;est mon premier Devoxx France, je compte bien en profiter au maximum (même si j&#8217;ai la pression jusqu&#8217;au vendredi am :) )</p>
</div>
<div class="paragraph">
<p>See you au Palais des Congrès !</p>
</div>
</div>
</div>]]></description><link>https://mgreau.com/posts/2015/04/08/agenda-asciidoctor-devoxxfr-tshirt-a-gagner.html</link><guid isPermaLink="true">https://mgreau.com/posts/2015/04/08/agenda-asciidoctor-devoxxfr-tshirt-a-gagner.html</guid><category><![CDATA[Asciidoctor]]></category><category><![CDATA[ AsciiDoc]]></category><category><![CDATA[ DevoxxFR]]></category><category><![CDATA[ talk]]></category><dc:creator><![CDATA[Maxime Gréau]]></dc:creator><pubDate>Wed, 08 Apr 2015 00:00:00 GMT</pubDate></item><item><title><![CDATA[How Asciidoctor uses Arquillian and Docker to verify that AsciidoctorJ works in WildFly AS]]></title><description><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Language <strong><em>EN</em></strong> /  Timereading <strong><em>10 mn</em></strong>
</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="title">Overview</div>
<div class="paragraph">
<p>This post explains how we execute Arquillian tests for AsciidoctorJ to a Docker container, so we&#8217;ll talk about:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="http://github.com/asciidoctor/docker-asciidoctorj">docker-asciidoctorj</a>, the dedicated github project on Asciidoctor organization</p>
</li>
<li>
<p><a href="https://registry.hub.docker.com/repos/asciidoctor/">the Asciidoctor organization on Docker Hub</a>,</p>
</li>
<li>
<p><a href="https://github.com/arquillian/arquillian-cube">Arquillian Cube</a>, the Arquillian extension that can be used to <strong>manage Docker containers from Arquillian</strong>.</p>
</li>
<li>
<p><a href="https://registry.hub.docker.com/u/jboss/wildfly/">the WildFly docker image</a> from JBoss</p>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>It&#8217;s been a while since I wrote here but like many technical bloggers (I guess), I have a lot of blog posts on draft mode. And I have a lot to write about 2014 which was
an <strong>AMAZING year</strong> : indeed, thanks to the <a href="http://asciidoctor.org"><strong>Asciidoctor project</strong></a> and Java EE 7 / WildFly technologies, I gave <strong>2 talks</strong> at <a href="http://www.devnation.org/2014/#websocketAsciidoctor">DevNation, San Francisco</a> to speak about Open Source projects:) and I <a href="http://mgreau.com/resume/resume.html#technical-writing">reviewed a book</a>.<br>
But I&#8217;ll blogged about this awesome experience soon.</p>
</div>
<div class="paragraph">
<p><strong>So, what&#8217;s this post about : Docker? Asciidoctor? Arquillian?</strong></p>
</div>
<div id="docker_arquillian_asciidoctor" class="imageblock">
<div class="content">
<img src="https://mgreau.com/posts/images/posts/docker-asciidoctorj/docker-arquillian-asciidoctor.png" alt="AsciidoctorJ Docker containers managed by Arquillian" width="750">
</div>
<div class="title">Figure 1. The power of Arquillian &amp; Docker for AsciidoctorJ integration tests</div>
</div>
<div class="paragraph">
<p>You have just read the title and you said:<br></p>
</div>
<div class="ulist">
<ul>
<li>
<p><em>"Oh no, Not another post about Docker!"</em>,</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>and I want to answer<br></p>
</div>
<div class="ulist">
<ul>
<li>
<p><em>"Well, it&#8217;s 2015, is it possible to write a blog post without mentioning Docker ?</em>"<br></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>To be serious, it&#8217;s more a blog post about the <a href="https://github.com/asciidoctor/asciidoctorj">AsciidoctorJ</a> project and how <a href="http://docker.com">Docker</a> helps us to easily check that it&#8217;s possible to execute AsciidoctorJ in a <strong>Java EE application server</strong> like <a href="http://wildfly.org/">WildFly</a>.<br>
So it&#8217;s not a post about Docker itself but more a use case where Docker is the key to the ease of tests&#8230;&#8203;OK you&#8217;re right, it&#8217;s about Docker:)</p>
</div>
</div>
<div id="toc" class="toc">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#_asciidoctor_on_the_jvm_with_asciidoctorj">Asciidoctor on the JVM with AsciidoctorJ</a></li>
<li><a href="#_asciidoctorj_in_a_java_application_server">AsciidoctorJ in a Java application server</a>
<ul class="sectlevel2">
<li><a href="#_how_to_make_it_work">How to make it work?</a></li>
<li><a href="#_make_it_easy_thanks_to_the_power_of_arquillian_docker">Make it easy thanks to the power of Arquillian &amp; Docker</a>
<ul class="sectlevel3">
<li><a href="#_arquillian_cube_extension_to_the_rescue">Arquillian Cube extension to the rescue!</a></li>
<li><a href="#_pull_the_official_docker_image_execute_tests_and_see_the_output">Pull the official Docker image, execute tests and see the output</a></li>
<li><a href="#_docker_asciidoctorj_project_in_detail">docker-asciidoctorj project in detail</a>
<ul class="sectlevel4">
<li><a href="#_customize_build_with_docker_and_use_your_own_docker_image">Customize, build with Docker and use your own Docker image</a></li>
<li><a href="#_all_in_one_with_arquillian_build_the_docker_image_and_execute_tests_in_a_container">All in one with Arquillian: Build the Docker image and execute tests in a container</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><a href="#_asciidoctorj_in_your_favorite_java_application_server">AsciidoctorJ in your favorite Java application server?</a></li>
<li><a href="#_conclusion_asciidoctor_on_dockerhub">Conclusion : Asciidoctor on DockerHub</a></li>
</ul>
</div>
</div>
<div class="sect1">
<h2 id="_asciidoctor_on_the_jvm_with_asciidoctorj">Asciidoctor on the JVM with AsciidoctorJ</h2>
<div class="sectionbody">
<div class="paragraph">
<p>I guess that I don&#8217;t need to introduce <a href="http://asciidoctor.org"><strong>Asciidoctor</strong></a> (started in 2012) now that it became so popular thanks to the awesome work done by <a href="https://twitter.com/mojavelinux"><strong>Dan Allen</strong></a> and the community.<br>
Well, if you don&#8217;t know, Asciidoctor is a <strong>fast text processor</strong> and <strong>publishing toolchain</strong> for <strong>converting</strong> <strong>AsciiDoc</strong> content to <strong>HTML5</strong>, <strong>PDF</strong>, <strong>DocBook 5</strong> (or 4.5) and other formats.
Asciidoctor is written in <strong>Ruby</strong>, packaged as a RubyGem and published to RubyGems.org.</p>
</div>
<div id="asciidoctor_processor" class="imageblock">
<div class="content">
<img src="https://mgreau.com/posts/images/posts/docker-asciidoctorj/asciidoctor-processor.png" alt="Diagram showing output files that Asciidoctor can generate" width="750">
</div>
<div class="title">Figure 2. Asciidoctor convert AsciiDoc files to several formats</div>
</div>
<div class="paragraph">
<p><strong>AsciidoctorJ</strong> is the official library, started by <a href="https://twitter.com/alexsotob">Alex Soto</a>, for running Asciidoctor on the JVM.
Using AsciidoctorJ, you can convert AsciiDoc content or analyze the structure of a parsed AsciiDoc document from <strong>Java</strong> and <strong>other JVM languages</strong>.</p>
</div>
<div id="asciidoctorj_jvm" class="imageblock">
<div class="content">
<img src="https://raw.githubusercontent.com/mgreau/slides/master/websocket-asciidoctor/NantesJUG/images/asciidoctor-java.png" alt="Diagram which explains how AsciidoctorJ makes Asciidoctor to work on the JVM" width="750">
</div>
<div class="title">Figure 3. Asciidoctor on the JVM with AsciidoctorJ</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_asciidoctorj_in_a_java_application_server">AsciidoctorJ in a Java application server</h2>
<div class="sectionbody">
<div class="paragraph">
<p>So here we have a problem.<br>
The problem is that if you want to include <strong>AsciidoctorJ</strong> in your Java application, it doesn&#8217;t work easily.<br>
Indeed, AsciidoctorJ is based on <strong>JRuby</strong> and there are some classloader troubles with JRuby as you can read in those different discussions: <a href="https://github.com/asciidoctor/asciidoctorj/pull/118">PR118</a>,  <a href="https://github.com/asciidoctor/asciidoctorj/issues/102">Issue 102</a>
and <a href="http://discuss.asciidoctor.org/AsciidoctorJ-1-5-and-Java-EE-td1651.html">this thread</a> on the asciidoctor discussion list.<br>
If you tried to include it without success, you should have seen the message below:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>Caused by: org.jruby.exceptions.RaiseException: (LoadError) no such file to load -- asciidoctor</pre>
</div>
</div>
<div class="sect2">
<h3 id="_how_to_make_it_work">How to make it work?</h3>
<div class="paragraph">
<p>The solution is to create a new <strong>JBoss module</strong> with all <strong>AsciidoctorJ dependencies</strong> and
then depend on this module in the target application.<br>
The <strong>module.xml</strong> file, which describes the Asciidoctor JBoss module, includes the following JARs:</p>
</div>
<div id="eg1-callouts" class="exampleblock">
<div class="title">Example 1. Asciidoctor JBoss module descriptor</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;module xmlns="urn:jboss:module:1.0" name="org.asciidoctor"&gt;
  &lt;resources&gt;
    &lt;resource-root path="asciidoctorj-1.5.2.jar"/&gt;
    &lt;resource-root path="asciidoctorj-pdf-1.5.0-alpha.6.jar"/&gt;
    &lt;resource-root path="jruby-complete-1.7.16.1.jar"/&gt;
    ...
  &lt;/resources&gt;

  &lt;dependencies&gt;
   ...
  &lt;/dependencies&gt;
&lt;/module&gt;</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>So you can follow all <em>those manual steps</em> describe in <a href="https://github.com/asciidoctor/asciidoctorj#wildfly-kludge">the documentation</a> to configure your WildFly installation.</p>
</div>
</div>
<div class="sect2">
<h3 id="_make_it_easy_thanks_to_the_power_of_arquillian_docker">Make it easy thanks to the power of Arquillian &amp; Docker</h3>
<div class="paragraph">
<p>OK well, we have a solution but there are several manual steps to follow and it&#8217;s not efficient to do all those steps to test each new version of
AsciidoctorJ or WildFly. Fortunately, the <strong>Arquillian Cube extension</strong> came into being! :)</p>
</div>
<div class="sect3">
<h4 id="_arquillian_cube_extension_to_the_rescue">Arquillian Cube extension to the rescue!</h4>
<div class="paragraph">
<p><strong>Arquillian Cube</strong> is a very nice project developed by <a href="https://twitter.com/alexsotob">Alex Soto</a>, <a href="https://twitter.com/aslakknutsen">Aslak Knutsen</a> and the Arquillian community.<br>
With Arquillian Cube you can <strong>control the lifecycle of Docker images</strong> as part of the <strong>test lifecycle</strong>, either automatically or manually.<br>
This gives you the chance to scale up from a integration/functional test level all the way up to the system test level.<br></p>
</div>
<div class="paragraph">
<p>This project gives you a lot of possibilities, if you want know more about all those features you can read <a href="http://arquillian.org/blog/2015/02/25/arquillian-cube-1-0-0-Alpha4/">this news on the arquillian website</a> or the
<a href="https://github.com/arquillian/arquillian-cube">documentation on the github project</a>.</p>
</div>
</div>
<div class="sect3">
<h4 id="_pull_the_official_docker_image_execute_tests_and_see_the_output">Pull the official Docker image, execute tests and see the output</h4>
<div class="paragraph">
<p>To quickly test the project, just follow the steps below:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>git clone https://github.com/asciidoctor/docker-asciidoctorj.git &amp;&amp; cd docker-asciidoctorj
mvn clean test -Pwildfly82
ls /tmp/documents</pre>
</div>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<div class="title">Prerequisites</div>
<div class="paragraph">
<p>Ensure that Java, Maven and Docker are installed.<br>
If you are using boot2docker, you can pass some configuration to the Maven command like this:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>mvn clean test -Pwildfly82 -Ddocker.serverProtocol=https  -Ddocker.serverIp=192.168.59.103 -Ddocker.serverPort=2376</pre>
</div>
</div>
<div class="paragraph">
<p><em>If you need help, you can read the Arquillian Cube <a href="https://github.com/arquillian/arquillian-cube#preliminaries">documentation about boot2docker configuration</a>.</em></p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Thanks to Arquillian Cube, the Maven command will do the following steps:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Download the AsciidoctorJ WildFly Docker image asciidoctor/asciidoctorj-wildfly from the DockerHub registry</p>
</li>
<li>
<p>Install this image in your local registry</p>
</li>
<li>
<p>Start a container</p>
</li>
<li>
<p>Connect to WildFly</p>
</li>
<li>
<p>Deploy app and Execute tests</p>
</li>
<li>
<p>Disconnect from WildFly</p>
</li>
<li>
<p>Copy the generated files from the container location to the host location</p>
</li>
<li>
<p>Stop the container</p>
</li>
<li>
<p>Destroy the container</p>
</li>
</ol>
</div>
<div id="cube_docker_process" class="imageblock">
<div class="content">
<img src="https://mgreau.com/posts/images/posts/docker-asciidoctorj/cube-docker-process.png" alt="Diagram with interaction between Cube and Docker container" width="750">
</div>
<div class="title">Figure 4. Steps done thanks to Arquillian Cube</div>
</div>
<div id="eg8-callouts" class="listingblock">
<div class="title">Tests executed with Maven</div>
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">[INFO]
[INFO] ------------------------------------------------------------------
[INFO] Building Docker AsciidoctorJ 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]

 -------------------------------------------------------
 T E S T S
 -------------------------------------------------------
Running org.asciidoctor.ConverterServletTest
Mar 01, 2015 10:31:47 PM org.jboss.arquillian.container.impl.MapObject populate
WARNING: Configuration contain properties not supported by the backing object org.jboss.as.arquillian.container.remote.RemoteContainerConfiguration
Unused property entries: {host=127.0.0.1, target=wildfly:8.2.0.Final:remote}
Supported property names: [managementAddress, password, managementPort, managementProtocol, username]
Mar 01, 2015 10:32:39 PM org.xnio.Xnio &lt;clinit&gt;
INFO: XNIO version 3.2.0.Beta4
Mar 01, 2015 10:32:39 PM org.xnio.nio.NioXnio &lt;clinit&gt;
INFO: XNIO NIO Implementation Version 3.2.0.Beta4
Mar 01, 2015 10:32:39 PM org.jboss.remoting3.EndpointImpl &lt;clinit&gt;
INFO: JBoss Remoting version (unknown)
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 65.087 sec - in org.asciidoctor.ConverterServletTest

Results :

Tests run: 3, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------</code></pre>
</div>
</div>
<div class="paragraph">
<p>Those tests are very simple for now, they will convert the same AsciiDoc file (sample.adoc) to a one PDF file and one HTML file.</p>
</div>
<div id="sample_files_output" class="imageblock">
<div class="content">
<img src="https://mgreau.com/posts/images/posts/docker-asciidoctorj/sample-pdf-html-output.png" alt="Screenshot showing both sample PDF and HTML files" width="750">
</div>
<div class="title">Figure 5. same sample.adoc file converted to PDF and HTML5 files by AsciidoctorJ on WildFly</div>
</div>
</div>
<div class="sect3">
<h4 id="_docker_asciidoctorj_project_in_detail">docker-asciidoctorj project in detail</h4>
<div class="paragraph">
<p>The project docker-asciidoctorj was created to test different versions of AsciidoctorJ, AsciidoctorJ PDF and others, quickly in the same environment.<br>
Some of the most important files of the project are described on the following lines.</p>
</div>
<div class="paragraph">
<p>The project layout is as follows:</p>
</div>
<div id="eg2-callouts" class="exampleblock">
<div class="title">Example 2. project structure</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">+ dockerfiles
   |+ wildfly82
      |- Dockerfile   <i class="conum" data-value="1"></i><b>(1)</b>
+ src/main/java
   |+ org.asciidoctor
      |- AsciidoctorProcessor.java    <i class="conum" data-value="2"></i><b>(2)</b>
      |- ConverterServlet.java   <i class="conum" data-value="3"></i><b>(3)</b>
+ src/main/resources
   |+ adoc
      |- sample.adoc   <i class="conum" data-value="4"></i><b>(4)</b>
+ src/test/java
   |+ org.asciidoctor
      |- ConverterServletTest.java   <i class="conum" data-value="5"></i><b>(5)</b>
+ src/test/resources
   |+ wildfly
      |- MANIFEST.MF   <i class="conum" data-value="6"></i><b>(6)</b>
   |- arquillian.xml   <i class="conum" data-value="7"></i><b>(7)</b>
pom.xml</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The Dockerfile used to build Docker image with AsciidoctorJ in WildFly 8.2</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Java Bean which converts AsciiDoc file to HTML or PDF file</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Java Servlet which uses the converter</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Sample AsciiDoc file to convert</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Tests to process AsciiDoc to HTML/PDF with AsciidoctorJ</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>MANIFEST file used to depend on AsciidoctorJ WilFly AS module ( Dependencies: org.asciidoctor )</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>Arquillian XML file to configure Docker containers</td>
</tr>
</table>
</div>
</div>
</div>
<div class="paragraph">
<p>The whole Dockerfile is described below:</p>
</div>
<div id="eg3-calouts" class="exampleblock">
<div class="title">Example 3. Dockerfile</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">FROM jboss/wildfly:8.2.0.Final  <i class="conum" data-value="1"></i><b>(1)</b>
MAINTAINER Maxime Gréau &lt;greau [dot] maxime [at] gmail&gt;

# Create a WildFly admin user to deploy app with CLI
RUN /opt/jboss/wildfly/bin/add-user.sh -up mgmt-users.properties admin Admin#70365 --silent  <i class="conum" data-value="2"></i><b>(2)</b>

# Set env variables for versions  <i class="conum" data-value="3"></i><b>(3)</b>
ENV ASCIIDOCTORJ_VERSION 1.5.2
ENV ASCIIDOCTORJ_PDF_VERSION 1.5.0-alpha.6
ENV ASCIIDOCTORJ_EPUB3_VERSION 1.5.0-alpha.4
ENV JRUBY_VERSION 1.7.16.1

# Handle asciidoctor-backends  <i class="conum" data-value="4"></i><b>(4)</b>
ENV ASCIIDOCTOR_BACKENDS /opt/jboss/asciidoctor-backends
RUN mkdir -p ${ASCIIDOCTOR_BACKENDS}

# Create the AsciidoctorJ module
RUN mkdir -p ${JBOSS_HOME}/modules/org/asciidoctor/main
ENV ASCIIDOCTORJ_MODULE /opt/jboss/wildfly/modules/org/asciidoctor/main

# Output directory to store generated files
ENV OUTPUT_DIRECTORY /tmp/documents
RUN mkdir -p ${OUTPUT_DIRECTORY}

# Set the ULR_BASE env variable to download artifacts
ENV URL_BASE https://repo1.maven.org/maven2/

ADD module.xml ${ASCIIDOCTORJ_MODULE}/module.xml     <i class="conum" data-value="5"></i><b>(5)</b>

RUN cd ${ASCIIDOCTORJ_MODULE} \  <i class="conum" data-value="6"></i><b>(6)</b>
&amp;&amp; curl -O ${URL_BASE}org/asciidoctor/asciidoctorj/${ASCIIDOCTORJ_VERSION}/asciidoctorj-${ASCIIDOCTORJ_VERSION}.jar \
&amp;&amp; curl -O ${URL_BASE}org/asciidoctor/asciidoctorj-pdf/${ASCIIDOCTORJ_PDF_VERSION}/asciidoctorj-pdf-${ASCIIDOCTORJ_PDF_VERSION}.jar \
&amp;&amp; curl -O ${URL_BASE}org/asciidoctor/asciidoctorj-epub3/${ASCIIDOCTORJ_EPUB3_VERSION}/asciidoctorj-epub3-${ASCIIDOCTORJ_EPUB3_VERSION}.jar \
&amp;&amp; curl -O -m 900 ${URL_BASE}org/jruby/jruby-complete/${JRUBY_VERSION}/jruby-complete-${JRUBY_VERSION}.jar \
\
&amp;&amp; (curl -LkSs https://api.github.com/repos/asciidoctor/asciidoctor-backends/tarball | tar xfz - -C ${ASCIIDOCTOR_BACKENDS} --strip-components=1)

WORKDIR ${OUTPUT_DIRECTORY}
VOLUME ${OUTPUT_DIRECTORY}  <i class="conum" data-value="7"></i><b>(7)</b>

CMD ["/opt/jboss/wildfly/bin/standalone.sh", "-b", "0.0.0.0", "-bmanagement", "0.0.0.0"]  <i class="conum" data-value="8"></i><b>(8)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>This image is based on the official WildFly 8.2 image</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Add an admin user, used in arquillian.xml file to deploy application</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Define variables for AsciidoctorJ core versions</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Add backend files (templates) used to generate HTML5 presentations from AsciiDoc for example</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Initialize the Asciidoctor JBoss Module with module.xml file</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>Add all AsciidoctorJ dependencies to the JBoss Module Ascciidoctor</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>Define a volume where the converted files will be copied</td>
</tr>
<tr>
<td><i class="conum" data-value="8"></i><b>8</b></td>
<td>Start a WildFly instance with asciidoctor module</td>
</tr>
</table>
</div>
</div>
</div>
<div class="paragraph">
<p>The arquillian.xml file is explained here:</p>
</div>
<div id="eg4-callouts" class="exampleblock">
<div class="title">Example 4. arquillian.xml with containers configuration</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;arquillian&gt;
...
&lt;extension qualifier="docker"&gt;
        &lt;property name="serverVersion"&gt;${docker.serverVersion}&lt;/property&gt;  <i class="conum" data-value="1"></i><b>(1)</b>
        &lt;property name="serverUri"&gt;${docker.serverProtocol}://${docker.serverIp}:${docker.serverPort}&lt;/property&gt; <i class="conum" data-value="2"></i><b>(2)</b>
        &lt;property name="autoStartContainers"&gt;${arquillian.cube.autostart}&lt;/property&gt;
        &lt;property name="shouldAllowToConnectToRunningContainers"&gt;true&lt;/property&gt;
        &lt;property name="dockerContainers"&gt;
            wildfly82_dockerfile:        <i class="conum" data-value="3"></i><b>(3)</b>
              buildImage:
                dockerfileLocation: dockerfiles/wildfly82   <i class="conum" data-value="4"></i><b>(4)</b>
                noCache: true
                remove: true
              exposedPorts: [8080/tcp, 9990/tcp]
              await:
                strategy: polling
                sleepPollingTime: 50000
                iterations: 5
              beforeStop:
                - copy:
                    from: /tmp/documents
                    to: /tmp
              portBindings: [8081-&gt;8080/tcp, 9991-&gt;9990/tcp]
            wildfly82:                                          <i class="conum" data-value="5"></i><b>(5)</b>
              image: asciidoctor/asciidoctorj-wildfly:latest   <i class="conum" data-value="6"></i><b>(6)</b>
              await:
                strategy: polling
                sleepPollingTime: 50000
                iterations: 5
              beforeStop:                      <i class="conum" data-value="7"></i><b>(7)</b>
                - copy:
                    from: /tmp/documents
                    to: /tmp
              portBindings: [8081-&gt;8080/tcp, 9991-&gt;9990/tcp]
        &lt;/property&gt;
    &lt;/extension&gt;

   &lt;container qualifier="wildfly82" default="true"&gt;  <i class="conum" data-value="8"></i><b>(8)</b>
       &lt;configuration&gt;
            &lt;property name="managementAddress"&gt;dockerServerIp&lt;/property&gt; <i class="conum" data-value="9"></i><b>(9)</b>
            &lt;property name="username"&gt;admin&lt;/property&gt;        <i class="conum" data-value="10"></i><b>(10)</b>
            &lt;property name="password"&gt;Admin#70365&lt;/property&gt;
       &lt;/configuration&gt;
   &lt;/container&gt;
   &lt;container qualifier="wildfly82_dockerfile" default="true"&gt;
       &lt;configuration&gt;
            &lt;property name="managementAddress"&gt;dockerServerIp&lt;/property&gt;
            &lt;property name="username"&gt;admin&lt;/property&gt;
            &lt;property name="password"&gt;Admin#70365&lt;/property&gt;
       &lt;/configuration&gt;
   &lt;/container&gt;
&lt;/arquillian&gt;</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The docker server version which expose the REST API required by Arquillian</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>URL to connect to Docker (Maven properties with default values)</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Container configuration to use with a Dockerfile</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Path where Arquillian will find the Dockerfile to use to build the image</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Container configuration name</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>The official asciidoctor image to use for the tests</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>Before stopping the container, Arquillian Cube will copy the generated files from container location to the host location</td>
</tr>
<tr>
<td><i class="conum" data-value="8"></i><b>8</b></td>
<td>Container configuration to work with the Docker image presents in the registry</td>
</tr>
<tr>
<td><i class="conum" data-value="9"></i><b>9</b></td>
<td>dockerServerIp will be auto resolved for all docker envs (boot2docker..)</td>
</tr>
<tr>
<td><i class="conum" data-value="10"></i><b>10</b></td>
<td>Arquillian use this admin user defined in the Dockerfile to communicate with WildFly</td>
</tr>
</table>
</div>
</div>
</div>
<div class="paragraph">
<p>Thanks to the JBoss Module we can create <strong>an instance of the Asciidoctor</strong> class without problem :</p>
</div>
<div id="eg5-callouts" class="exampleblock">
<div class="title">Example 5. Process AsciiDoc: AsciidoctorProcessor.java</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">....
@Named
public class AsciidoctorProcessor {

	private Asciidoctor asciidoctor;

	@Inject
	private Logger logger;

	@PostConstruct
	public void init() {
    asciidoctor = Asciidoctor.Factory.create();  <i class="conum" data-value="1"></i><b>(1)</b>
	}

	public Path convertToHTML(final String inputFilename) throws Exception {
    asciidoctor.convert(getResourceAsString(inputFilename),  <i class="conum" data-value="2"></i><b>(2)</b>
            parameters(getOutputDir(), "sample.html", "html5"));
    return FileSystems.getDefault().getPath(getOutputDir() + "sample.html");
	}

	public Path convertToPDF(final String inputFilename) throws Exception {
        String outputFilename = "sample.pdf";
        asciidoctor.convert(getResourceAsString(inputFilename),
                parameters(getOutputDir(), outputFilename, "pdf"));
        return FileSystems.getDefault().getPath(getOutputDir() + outputFilename);
	}

	private Map&lt;String, Object&gt; parameters(String outputDir,    <i class="conum" data-value="3"></i><b>(3)</b>
			  String outputFilename, String backend) {
		    return options().backend(backend).safe(SafeMode.UNSAFE)
				      .headerFooter(true).inPlace(true)
				      .toFile(new File(outputDir + outputFilename)).asMap();
  }

        public static final String getOutputDir() {
            return System.getenv("OUTPUT_DIRECTORY") + "/";   <i class="conum" data-value="4"></i><b>(4)</b>
        }
    ...</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Create the Asciidoctor instance to be able to process AsciiDoc</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Convert the sample.adoc file (present in the classpath) to a sample.html file</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Configure options and attributes to process the AsciiDoc</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>This environnment variable <strong>OUTPUT_DIRECTORY</strong> is defined in the Dockerfile (/tmp/documents)</td>
</tr>
</table>
</div>
</div>
</div>
<div class="paragraph">
<p>The following test for PDF converter is pretty simple:</p>
</div>
<div id="eg6-callouts" class="exampleblock">
<div class="title">Example 6. Convert AsciiDoc to PDF: ConverterServletTest.java</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">    @Test
    public void should_convert_to_pdf(@ArquillianResource URL base) throws IOException {
        URL url = createURL(base, "convert", "pdf", "sample.adoc");
        assertThat(getResponse(url), is("PDF : true"));
    }</code></pre>
</div>
</div>
</div>
</div>
<div class="sect4">
<h5 id="_customize_build_with_docker_and_use_your_own_docker_image">Customize, build with Docker and use your own Docker image</h5>
<div class="paragraph">
<p>You can customize the existing Dockerfile, then build the image with Docker and finally use this image
with Arquillian when you execute the test:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Clone this project:</p>
<div class="literalblock">
<div class="content">
<pre>git clone https://github.com/asciidoctor/docker-asciidoctorj.git</pre>
</div>
</div>
</li>
<li>
<p>Customize the dockerfiles/wildfly82/Dockerfile</p>
</li>
<li>
<p>Build the Docker image</p>
<div class="literalblock">
<div class="content">
<pre>cd docker-asciidoctorj
docker build -t asciidoctor/asciidoctorj-wildfly dockerfiles/wildfly82/</pre>
</div>
</div>
</li>
</ol>
</div>
<div id="eg7-callouts" class="exampleblock">
<div class="title">Example 7. Docker images</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">mgreau@mgreau-osxubuntu:~/git/asciidoctor/docker-asciidoctorj$ docker images
REPOSITORY                          TAG           IMAGE ID       CREATED        VIRTUAL SIZE
asciidoctor/asciidoctorj-wildfly    latest        80e19c9457fc   2 minutes ago  982.5 MB
jboss/wildfly                       8.2.0.Final   24c5b96027df   3 months ago   951.3 MB
---</code></pre>
</div>
</div>
</div>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Execute the Maven command below:</p>
<div class="literalblock">
<div class="content">
<pre>mvn clean test -Pwildfly82</pre>
</div>
</div>
</li>
</ol>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="literalblock">
<div class="content">
<pre>Until the Docker image is present in your registry, you just have to execute the Maven command.</pre>
</div>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect4">
<h5 id="_all_in_one_with_arquillian_build_the_docker_image_and_execute_tests_in_a_container">All in one with Arquillian: Build the Docker image and execute tests in a container</h5>
<div class="paragraph">
<p>You can do the same thing but with only one Maven command! Indeed, Arquillian Cube will build the Docker image for you and
then execute tests in the container:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Clone this project:</p>
<div class="literalblock">
<div class="content">
<pre>git clone https://github.com/asciidoctor/docker-asciidoctorj.git</pre>
</div>
</div>
</li>
<li>
<p>Customize the dockerfiles/wildfly82/Dockerfile</p>
</li>
<li>
<p>Execute the Maven command</p>
<div class="literalblock">
<div class="content">
<pre>mvn clean test -Pwildfly82_dockerfile</pre>
</div>
</div>
</li>
</ol>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_asciidoctorj_in_your_favorite_java_application_server">AsciidoctorJ in your favorite Java application server?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>It seems that the <strong>JRuby</strong> classloader problem is solved by the use of <strong>JBoss Modules components</strong>.<br>
However if you want to quickly test AsciidoctorJ in your favorite Java application server like TomEE or others&#8230;&#8203;just follow the steps below:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Fork the GitHub repository <a href="http://github.com/asciidoctor/docker-asciidoctorj">docker-asciidoctorj</a></p>
</li>
<li>
<p>Create a file named Dockerfile inside a subfolder of dockerfiles directory, following the named convention {appservername}{version}</p>
</li>
<li>
<p>Update the src/test/resources/arquillian.xml file to add a docker container configuration</p>
</li>
<li>
<p>Update the pom.xml to add Maven profiles related to the application server (dependencies&#8230;&#8203;)</p>
</li>
<li>
<p>Execute tests (maybe create a dedicated test) and see the results</p>
</li>
<li>
<p>If tests passed, then you could write documentation and send a PR</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>If you try it, I&#8217;d love to have your feedbacks, so feel free to add a comment below, on the <a href="http://discuss.asciidoctor.org/">discussion list</a> or on the <a href="http://github.com/asciidoctor/docker-asciidoctorj">GitHub project</a>.<br>
We are waiting for PR :)</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_conclusion_asciidoctor_on_dockerhub">Conclusion : Asciidoctor on DockerHub</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The good news here is that the Asciidoctor project now have an <strong>easy way to test</strong> if the future versions of <strong>AsciidoctorJ will be compatible</strong> with this architecture.<br>
But the most important thing is that Asciidoctor now has <strong>its own official registry for Docker images</strong>, with, for now, 2 officials images :</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://registry.hub.docker.com/u/asciidoctor/docker-asciidoctor">asciidoctor/docker-asciidoctor</a> <em>(created by <a href="https://twitter.com/g_scheibel">Guillaume Scheibel</a>)</em></p>
</li>
<li>
<p><a href="https://registry.hub.docker.com/u/asciidoctor/asciidoctorj-wildfly">asciidoctor/asciidoctorj-wildfly</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>And I&#8217;m so proud to have moved my first project to the Asciidoctor github organization and to be one of the Docker Hub administrator for Asciidoctor!<br></p>
</div>
<div class="paragraph">
<p>Have fun with <strong>Asciidoctor</strong>, <strong>Arquillian</strong> and <strong>Docker</strong> :)</p>
</div>
</div>
</div>]]></description><link>https://mgreau.com/posts/2015/03/03/docker-asciidoctorj-wildfly-arquillian.html</link><guid isPermaLink="true">https://mgreau.com/posts/2015/03/03/docker-asciidoctorj-wildfly-arquillian.html</guid><category><![CDATA[[EN]]]></category><category><![CDATA[ Asciidoctor]]></category><category><![CDATA[ docker]]></category><category><![CDATA[ WildFly]]></category><category><![CDATA[ Arquillian]]></category><dc:creator><![CDATA[Maxime Gréau]]></dc:creator><pubDate>Tue, 03 Mar 2015 00:00:00 GMT</pubDate></item><item><title><![CDATA[JBoss EAP 4.3 / 5.1 / 6.2 - Java EE supported standards in each EAP releases]]></title><description><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Language EN / Timereading 5 mn
</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="title">This post presents, as a diagram, all Java EE standards supported in each JBoss EAP version :</div>
<div class="ulist">
<ul>
<li>
<p>This diagram was updated in December 2013 (fixes and addition of (*) information)</p>
</li>
<li>
<p>Thanks to Arun Gupta for the fixes</p>
</li>
<li>
<p>The following data are based on official documentation from Oracle (JSR) and Red Hat (EAP).</p>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>You can <strong>click on each component to access the Java Specification Requests (JSRs)</strong> details or move to the complete lists.</p>
</div>
<div class="mxgraph" style="position:relative;overflow:hidden;width:1212px;height:935px;"><div style="width:1px;height:1px;overflow:hidden;">7V1tc+I4Ev41qbrbqqGsF0v2R4aEvZ3aqU1t5m72qwMOeJZgzphNZn/9Sn7DLYlXGeN4wlRlQAYbWo+7n251q2/I6Pn15yRYzT/H03Bxg53p6w25vcHYcxzxVw58VwZmSTTNh1AxsImm4RoMpXG8SKMVHJzEy2U4ScHYU7yAJ1sFs1AbeJgEC330azRN58WXw2w7/p8wms3LyyDm50fW6ffyHNPwKdgs0g/ZEM4PvzrFmfKX34uXmJbnXYLr/x3Hz2AgCdfR3/A7PkXFlygE9xgn0zABQ4to+WddSOTuhoySOE7zZ8+vo3AhZ6WUeP6x8Y6jlWyScJke8wGSf+CvYLEpvromq6dosRjFizj/3mQ89sXjhnxcp0n8Z1geWcbLsDhXmKTh687vs/2VAndh/BymyXcp/eJoIZYScu7AqT1Qcb6X7bwjXKJyXpt07tKBX3/kbwkKSc+q625FI54U0jFLip8sKcY+4vH4UpLCUDSOBwSHPEcTFaWKUKguOEYce1F5Z4BKwupSouK+D344Oigq4kPp4gtJCrGTReX7lxQV5c4RwjHcceeI47/rMPnt8Zu0CEIRBo/C/GQf/fQxXq/F0N0yTcJVEq3FL3OGq9UimgRpFC/Fq/tFkD7FybN4+q+74f2/b6T6J6+BkEuwnEoBblarWAhCPv8U/BXI092JPw+pOB4k0/VW/eYXnaeptFZD+WXxOJhMwvV6kITTeZAOJkLV4/Gfy/hlEU6FdRLHkzSaLITNw2OECOGFHq3m1atJuZjz2pQm8WY5DeV7kfjCqtrw5T8xPkuCaSTmDUy+RKN58sWJYvDmcfYoxh+Kq6Pi9Th4jhZykr8E8/g5KN+VWzDkNQAlQuBd5xGALEV/IR9rQHNdA9C803EmXtagdhz0hvfiLx0QE0wqlLy8vECIrJJ4upmkEhbfHsWZhKjCJEPwczSdLsIXIT2Jni2WP6wqJI8VDPkWGBKPDCcqhgqkXBw+4hLzYBq/ZB+xx5LPoFri1NWZgEkttYkWd4Cuh5bccJyHFkL6hRbX9eqax2MKdvDArWmeDiKJDfAVkUTOR5LjMDYa9QdJiKh6x+hXXBk7OGM2aEBrLGgkfnb0FEn+swdI3yarQZzMxLNwKV+uE/F3GqZBJM49zhCBXATxkRvl8/BRza2B21THIEgy1iN5j44UMH4aUmgTRknhykxxMwzg4CbmfFlwDP/48PVB0uSf6iQZDxxbYFSRkQoY6HxgjMTD1gTl6vxqcECO6wBK6wJ0UJXw6ugg7aPj4cuvNVSgA3bnCFQ4LlZoLLKwJ13whZrABkHYFE8onWykWJVuYGP4x+8AG9Yaw/FVNzkX7o+NDcoYmH1XwYaiNmgnsPH5AUDjsONzBDz88rdt4eG+w4Nhf0Br8DigOjoBj4fh8BPAx/4wyjGqg3EVG288xNYENrjPTJxih+pwSBew8TV8lGHYMPkrmsj4qfNZTPQ0SIMaYG7ksqAAgZPOw238dhvztfRoPKRiySbU1hMseRwyFHIyljxzvLddX+f3+9EJlukYtDgqWrCFA9wXtPgMn6B5EO8EafkybBYavqMaJWxhlEaj8fiOHesCF/4uY53zg4miSTgF2KA+H/AaoemEUSosjLRKizCtoYTYez0EuSpKLMyNkKc5gtZOhJU2HWElMGaCCHR/XNc1xVSujJb7YbOOcZUNVEGEWgRNxng8vr07SZEQYoqvXlmRcGXRz2VQseiawyNAtTB2BQby8TKxVjWqRi1c474AxGNgna/0fUtDQ9CA1xf6mI4XBJULN+EF8l7WghnKlnNGea6kdIMqJA2TyTxKxehGLuc5QAm51kQXaRhjbxtjuAklhL06AMrMmDKJxYHwoLp5Ki8JQAWDNp5/SVDdfZIqKSMyFVzyMSwpsDZ2CWtmQYt7Y81caM2EnTrkI8F3oKtrp4d7YNWs/SdMVWZMLZhxX2kPIwdpDwUR4ivQnoexuoqYcyBLfGhriVa5B31hPRwGW7ijJxp47ftEMvam5xk04ByRMut/iwKLRAN6W+SM9MR/lg4y4BKQn3A66IbLPBw1HZfVwiquhUvUO1goRgT5BCQ9GoiqA8P37VsRkO6fLRNVeNkuFzWZq4JKzbmFkIXH0zcIMceYzlayEuzWvWvUjTBukewGSWojVERNdkN5sOAdKpm2UZf+AFSyQEydoBqUz9VC/lmsxb1E4iymKmRY65mzjmPOnJULR2dT2Bp4nCbAw2HIn/l8sD9sh9hV0cJqaBlvslm8T2IxX9JuNQYfUtYGbuFjQXvPgw/CxK90jOIBXXHxGdGTE6+vhhh9SRE3EKp1NdWC363RlrhA++O6gPuaisiva37qEX5Q5Vo7cBuuFvH3ZykCNdJiiybH81Q00fPRNKICCB9PCrN0M42Bm/2h0rP2hPBrpMbgZbtGrVSHmH9RiN2Gj5vZLFrOpBrKy6EhpvIcu9/SeSj//zVYzjbBLMvJazjvW4vzMgtXvTd5MpzuA5jL8IAbUMXNIX+w+0Wby0tqVpU1WvSsKmbhavWisIj6eyoEqI+NiOhYwM8aF3rAj1ll2/UBF0qtiOd3r8Ts1Kge+skaKFpYj1ksK/ajMpECoDDyZjjwIQJsvY6gs19uEabpo1ohBK4dEHoceDy3BfCY/GtqrUE0/5pbxF76oUGwxk47xzlG8fNz5jcPl8s4zRTIWnN3dpQU7WMquEzd2cLBItzSl8w45pE9QX/iukBpGAwOMxbEXy1PDiaxNJAHR7R61rwU4jzY9C7xHytODTQ6HLmHYrrX2C4BZsBhW4joGXDcIjLSd4goK9CUd3BDDdXttecimtvLLZIUegcRDjCBlFiIgakiGD8rNytt0SEu8rGRlnvdYo42QZrXYxFk65/i2bNbC4Xb/BjUDvIP7rPrwz3oyjWp7pWVMGukYawizSJs17/yR3PJWqXPIIumhjQr12TkWiXO+f5zn4NlMAsvEKDh6noAtwjn9c8AAoBgqKuwh035v0bIdCoKbL1hjB4E9izCen1DDfJg5aQPaRQrd7vvUp11I6xHVSWeRVyvd6DwyT5QuC4G9bQd8be0jF97p1zL9fUswn19dMrrybxKri/mAwOEehEXtmYyehzZs9pruWfqhzsAWByqH+KSQ+GergWUs80gmtVMqi/l0XcAVR1xHGb0uyvV5B0sQ0DQLTclV11YVd3+0vDWMmXUdAuZ9/BhbV9emDpV+tcVD/a6F2L+GAbSlv0vWERTNUmiiXLbssB8C5j3yOCu7aocT0mVYFekP3p3qTK1ZV97qXA5HSZJJqB9c6JKMeuDtFeG4irxJpmU7+KFLk2DZBamYFCXdhNNpIqC9CYJnF6K7r2Horb2FzkwoRkqU8zdQ31X2rw3yo2U990bMzF/q52SKbpZBo/l253TJQZFgqEz5dFDdU3E4ZAz6/KjygZ7ZVKCVdu6snBhn/D6sg+qj81zWJsGpVSE47amAWnT8FDrexdJnmDoaOZkCi/vV1U8rzoO1SZQSCW1E1HxEQ/ycg/ycm4WTE28WNDyOsp9Xbxl91c7cZIOqAQ5FXWdAJMefAebSgG6oBLoYeH1zRz6ZO9c6uzvaorCPVVR2OuE/bLx3TeiE9j1dYKrdDOGPkeHWcIRPZPbqSVtgSXs8GS6cPPrXuCBm79BZrBDLKUWIG9CC2BHJ1pX0AL78q1VYqDbnitpAVymT52tBa6+P8ExSC8g0kUFgB2d1x6jAM696XeKorzp6Ru56Y9gtJe+6X2EgDuA3obpx45OOE+76RurkmnhzqfdvfN19npqgOB8LbBDLG/N9B9BYtvQArtzLjzfuD1AF7TAEesPFy+dbEEBcPPsdUEB+GcpgPNv+h2i6LTpP9SUfSj3NeaD1/zE+5pcyt7rxSJS9lx8CTyexi/LRRxM1/nnq3bJ/HH6SAkOAkId4k0+FJO1czEpmwWB+VUwiZazL7G49u0HVwE2O2qeDixy+nvKH7jHTPvSuNzknICJgbkV1L1seXh97tzLzx3an13c2txRDgNrRJm8Q02oOz2PtIV5RN2YR4/TPQ32uGc06F2eR5nL3fzm8FqvEcOM4r0z+kP2DHAZbByrb9/gGRiKsqGd551uak9BjN5upJGdnNWSawNiLLJy+4MYJRVO6UKCNcRwAxlrve2IJTbKXU72YIO+Y4O7oHpIyYEz9iOBvJ8bHNA2GvM1WUuk5WIbsPLeow9zhvbwGLVHn6EJluI5e61rGXOZvkJlDhTrV2Nt8B2L/O4+dYas82MFdSe3XivdqOvsjXdEzb5KrJvvBmkAmkVeeF82wVKJNRLAOdBnB7PDCq3NMiWb6rjDDp1W82bAkcV+EL3BEfNNgc2q9o29tc3Usj0hKmDs2BvCmoRpe0MY0LU/qPqDbx6Nlf7q2LDBiKnxKDf2vb2cKfwsJ13ZV6uJaICP/EMAwhb7RvQnGgDjjRSCyMMH1JOy3VEnGm2bIHV5ML0HIzGncIkCKWCCjTUM3PwdTAWY3ncPqO8eoBBxaNhMdg3CsFz/aKH4zDCVepZJZt2cjJlL0v3p4Xf5MileCmGtyyZBToYrGGaonv4i0focBpLUCxofpBWbz9f76yeqPrTOcwTWxcWW4Yv+DWpvz49vz/QyjyZz+XvlCeKNvGT8VF0Xfs//b2K5ojbZ9k1z1pN4FW4PDXLRnLoK5zSAKsWZKzv6lYk3gjzVraKpWwvMVyBYxxhSM3f5OZhL4jitHfs5CVbzz/FU6qi7fwA=</div></div>
<script src="//drawdotio.appspot.com/embed.js" type="text/javascript"></script>
</div>
</div>
<div class="sect1">
<h2 id="_list_of_all_java_ee_jsrs_supported">List of all Java EE JSRs supported</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_supported_in_jboss_eap_4_3_5_6">Supported in JBoss EAP 4.3 / 5 /6</h3>
<div class="ulist">
<ul>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=052">JSR 052: A Standard Tag Library for JavaServer Pages</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=067">JSR 067: JavaTM APIs for XML Messaging 1.0</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=093">JSR 093: JavaTM API for XML Registries 1.0 (JAXR)</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=101">JSR 101: JavaTM APIs for XML based RPC</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=153">JSR 153: Enterprise JavaBeansTM 2.1</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=181">JSR 181: Web Services Metadata for the JavaTM Platform</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=220">JSR 220: Enterprise JavaBeansTM 3.0</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=914">JSR 914: JavaTM Message Service (JMS) API</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=919">JSR 919: JavaMail</a></p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_supported_in_jboss_eap_4_3">Supported in JBoss EAP 4.3</h3>
<div class="ulist">
<ul>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=151">JSR 151: JavaTM 2 Platform, Enterprise Edition 1.4 (J2EE 1.4) Specification</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=088">JSR 88: JavaTM EE Application Deployment</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=109">JSR 109: Implementing Enterprise Web Services</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=115">JSR 115: JavaTM Authorization Contract for Containers</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=154">JSR 154: JavaTM Servlet 2.4 Specification</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=224">JSR 224: JavaTM API for XML-Based Web Services (JAX-WS) 2.0</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=907">JSR 907: JavaTM Transaction API (JTA)</a></p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_supported_in_jboss_eap_4_3_5_1">Supported in JBoss EAP 4.3 / 5.1</h3>
<div class="ulist">
<ul>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=077">JSR 077: J2EETM Management</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=112">JSR 112: J2EETM Connector Architecture 1.5</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=220">JSR 220: Enterprise JavaBeansTM 3.0</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=222">JSR 222: JavaTM Architecture for XML Binding (JAXB) 2.0</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=245">JSR 245: JavaServerTM Pages 2.1</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=250">JSR 250: Common Annotations for the JavaTM Platform</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=252">JSR 252: JavaServer Faces 1.2</a></p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_supported_in_jboss_eap_5_1">Supported in JBoss EAP 5.1</h3>
<div class="ulist">
<ul>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=244">JSR 244: JavaTM Platform, Enterprise Edition 5 (Java EE 5) Specification</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=109">JSR 109: Implementing Enterprise Web Services</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=115">JSR 115: JavaTM Authorization Contract for Containers</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=154">JSR 154: JavaTM Servlet 2.4 Specification</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=224">JSR 224: JavaTM API for XML-Based Web Services (JAX-WS) 2.0</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=311">JSR 311: JAX-RS: The JavaTM API for RESTful Web Services</a></p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_supported_in_jboss_eap_5_1_6">Supported in JBoss EAP 5.1 / 6</h3>
<div class="ulist">
<ul>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=045">JSR 045: Debugging Support for Other Languages</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=088">JSR 088: JavaTM EE Application Deployment</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=907">JSR 907: JavaTM Transaction API (JTA)</a></p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_supported_in_jboss_eap_6">Supported in JBoss EAP 6</h3>
<div class="ulist">
<ul>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=316">JSR 316: JavaTM Platform, Enterprise Edition 6 (Java EE 6) Specification</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=077">JSR 077: J2EETM Management</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=109">JSR 109: Implementing Enterprise Web Services</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=115">JSR 115: JavaTM Authorization Contract for Containers</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=222">JSR 222: JavaTM Architecture for XML Binding (JAXB) 2.0</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=224">JSR 224: JavaTM API for XML-Based Web Services (JAX-WS) 2.0</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=245">JSR 245: JavaServerTM Pages 2.1</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=250">JSR 250: Common Annotations for the JavaTM Platform</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=299">JSR 299: Contexts and Dependency Injection for the JavaTM EE platform</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=303">JSR 303: Bean Validation</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=311">JSR 311: JAX-RS: The JavaTM API for RESTful Web Services</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=314">JSR 314: JavaServer Faces 2.0</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=315">JSR 315: JavaTM Servlet 3.0 Specification</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=317">JSR 317: JavaTM Persistence 2.0</a></p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=322">JSR 322: JavaTM EE Connector Architecture 1.6</a></p>
</li>
</ul>
</div>
</div>
</div>
</div>]]></description><link>https://mgreau.com/posts/2013/12/10/jboss-eap-62-51-43-javaee-supported.html</link><guid isPermaLink="true">https://mgreau.com/posts/2013/12/10/jboss-eap-62-51-43-javaee-supported.html</guid><category><![CDATA[Java EE]]></category><category><![CDATA[ JBoss]]></category><category><![CDATA[ EAP]]></category><category><![CDATA[ JSR]]></category><dc:creator><![CDATA[Maxime Gréau]]></dc:creator><pubDate>Tue, 10 Dec 2013 00:00:00 GMT</pubDate></item><item><title><![CDATA[Java EE 7 and WebSocket API for Java (JSR 356) with AngularJS on WildFly]]></title><description><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<div class="title">This post was originally published in November 2013 and has been updated on February 3rd, 2016 for the following reasons:</div>
<div class="ulist">
<ul>
<li>
<p>Use the newest final  version of <strong>WildFly 10.0.0</strong></p>
</li>
<li>
<p>App deployed in a <strong>Docker</strong> Container at <a href="http://javaee7-websocket.mgreau.com" class="bare">http://javaee7-websocket.mgreau.com</a> thanks to <a href="https://www.clever-cloud.com">Clever Cloud</a> PaaS!</p>
</li>
<li>
<p><em>This app is not anymore available on OpenShift</em></p>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="title">Overview</div>
<div class="paragraph">
<p>This blog post describes the <strong><a href="http://jcp.org/en/jsr/detail?id=356">Java API for WebSocket Protocol (JSR 356)</a></strong> (which is one of four newest JSRs for the <strong><a href="http://jcp.org/en/jsr/detail?id=342">Java EE 7</a></strong> platform) and provides a concrete application deployed on a <a href="http://wildfly.org">WildFly 10</a> <strong>Docker Container</strong> and <a href="http://javaee7-websocket.mgreau.com">available online thanks to Clever Cloud </a>.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>[FR] La version française (<a href="http://mgreau.com/posts/2013/09/27/javaee7-api-websocket-html5.html">HTML</a> ou <a href="http://mgreau.com/doc/javaee7-api-websocket-html5.pdf">PDF</a>) de ce post est basée uniquement sur la démonstration avec l&#8217;API Javascript sans AngularJS.</p>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Once you will have read this post, you will be able to understand <a href="https://twitter.com/arungupta">Arun Gupta</a>'s definition about what is it possible to do with WebSocket technology.</p>
</div>
<div class="quoteblock">
<blockquote>
WebSocket gives you bidirectionnal, full duplex, communication channel over a single TCP.
</blockquote>
<div class="attribution">
&#8212; Arun Gupta (Java EE Evangelist at Oracle)<br>
<cite>Devoxx UK 2013</cite>
</div>
</div>
</div>
<div id="toc" class="toc">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#_java_ee_7_overview">Java EE 7 overview</a></li>
<li><a href="#_demo_angularjs_html5_jsr_356_api_application_deployed_on_wildfly_10_clever_cloud">DEMO : AngularJS - HTML5 / JSR-356 API application deployed on WildFly 10 (Clever Cloud)</a></li>
<li><a href="#_websocket_ws_a_new_protocol_different_from_http">WebSocket (WS): a new protocol different from HTTP</a>
<ul class="sectlevel2">
<li><a href="#_opening_handshake">Opening Handshake</a></li>
<li><a href="#_data_transfer">Data transfer</a></li>
<li><a href="#_closing_handshake">Closing Handshake</a></li>
</ul>
</li>
<li><a href="#_websocket_javascript_api_client">WebSocket Javascript API (Client)</a></li>
<li><a href="#_jsr_386_java_api_for_websocket_protocol">JSR 386 : Java API for WebSocket protocol</a>
<ul class="sectlevel2">
<li><a href="#_websocket_server_endpoint">WebSocket Server Endpoint</a></li>
<li><a href="#_annotations">Annotations</a></li>
<li><a href="#_encoders_and_decoders">Encoders and Decoders</a></li>
<li><a href="#_websocket_client_endpoint">WebSocket Client Endpoint</a></li>
</ul>
</li>
<li><a href="#_us_open_application">US OPEN Application</a>
<ul class="sectlevel2">
<li><a href="#_maven_dependencies_for_java_ee_7_api">Maven dependencies for Java EE 7 API</a></li>
<li><a href="#_add_server_endpoint">Add Server Endpoint</a></li>
<li><a href="#_encodes_and_decodes_messages">Encodes and Decodes messages</a></li>
<li><a href="#_html5_web_client_single_match_index_html">HTML5 Web Client (single match - index.html)</a></li>
<li><a href="#_angularjs_client_several_matches_live_html">AngularJS Client (several matches - live.html)</a></li>
<li><a href="#_source_code_on_github">Source code on Github</a></li>
<li><a href="#_build_and_deploy_the_war">Build and Deploy the WAR</a></li>
</ul>
</li>
<li><a href="#_benchmark_websocket_vs_rest">Benchmark : WebSocket VS REST</a></li>
<li><a href="#_references_about_websocket">References about WebSocket</a></li>
<li><a href="#_conclusion">Conclusion</a></li>
</ul>
</div>
</div>
<div class="sect1">
<h2 id="_java_ee_7_overview">Java EE 7 overview</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The <strong>Java Platform Enterprise Edition</strong> was released in Version 7 (Java EE 7) in <strong>June 2013</strong>.
In line with the two previous versions (Java EE 5 and Java EE 6) <strong>Java EE 7</strong> always proposes to simplify the work of the developer.
This version decorates previous versions with 3 main objectives :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>embraces <strong>HTML5</strong> (WebSocket API, JSON-P API, JAX-RS)</p>
</li>
<li>
<p>provide an <strong>even better productivity</strong> to developer (JMS)</p>
</li>
<li>
<p>meeting <strong>enterprise demands</strong> (Batch API, Concurrency Utilities)</p>
</li>
</ul>
</div>
<div id="javaee7_intro" class="imageblock">
<div class="content">
<img src="https://mgreau.com/posts/images/javaee7_intro.png" alt="Java EE 7 goals">
</div>
<div class="title">Figure 1. The 3 goals of Java EE 7</div>
</div>
<div class="paragraph">
<p>Java Platform, Entreprise Edition 7 (JSR 342) can be summmed up around :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>4 newest specifications : <strong>Java API for WebSocket 1.0</strong>, <strong>Java API for JSON Processing 1.0</strong> , <strong>Batch Applications 1.0</strong> and <strong>Concurrency Utilities for Java EE 1.0</strong></p>
</li>
<li>
<p>3 specifications with major updates : <strong>JMS 2.0</strong>, <strong>JAX-RS 2.0</strong> and <strong>EL 3.0</strong></p>
</li>
<li>
<p>and 6 specifications with minor updates : <strong>JPA 2.1</strong>, <strong>Servlet 3.1</strong>, <strong>EJB 3.2</strong>, <strong>CDI 1.1</strong>, <strong>JSF 2.2</strong> and <strong>Bean Validation 1.1</strong></p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_demo_angularjs_html5_jsr_356_api_application_deployed_on_wildfly_10_clever_cloud">DEMO : AngularJS - HTML5 / JSR-356 API application deployed on WildFly 10 (Clever Cloud)</h2>
<div class="sectionbody">
<div class="paragraph">
<p>If you want to see right away what it looks like, you can access {link-demo}[the online application] whose code will be in part explained in this article.
It&#8217;s an application that give you the ability :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>to watch one or several tennis matches in <strong>live mode</strong> (Quarter Final U.S. Open 2013)</p>
<div class="ulist">
<ul>
<li>
<p>click on each match that you want to access live</p>
</li>
<li>
<p>click on exit button to disconnect from a specific match</p>
</li>
</ul>
</div>
</li>
<li>
<p>to bet on the winner of the match</p>
<div class="ulist">
<ul>
<li>
<p>each time a user bet on the same match, the counter is incremented</p>
</li>
<li>
<p>at this end of the match, you will see the result of your bet</p>
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="paragraph">
<p>You will say: '"Nothing special !"', and you&#8217;re right :)</p>
</div>
<div class="paragraph">
<p>At first glance, it sounds like something already seen in many of today&#8217;s applications, but it&#8217;s the technique used behind which does matter because, as you will see below, everything is based around the <strong>standard of the new WebSocket protocol (ws:// ou wss://)</strong> and not on "HTTP hacking".</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="title">The technologies used for the development of this application are :</div>
<div class="ulist">
<ul>
<li>
<p>Frontend : HTML5, CSS, Javascript (WebSocket API) with 'Bootstrap CSS and AngularJS'</p>
</li>
<li>
<p>Backend : Java API for WebSocket, EJB, JSON-P, JAX-RS</p>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div>
<div id="websocket_example" class="imageblock">
<div class="content">
<a class="image" href="http://javaee7-websocket.mgreau.com"><img src="https://mgreau.com/posts/images/websocket_wildfly_angularjs_tennis.png" alt="Implementation of WebSocket (Java API et Javascript API)"></a>
</div>
<div class="title">Figure 2. US Open Application  - Implementation of WebSocket (Java API et Javascript API)</div>
</div>
<div class="paragraph">
<p>Nope! This demonstration is <strong>not a chat application :)</strong>
It&#8217;s obvious that the "chat demo" is the one that first comes to mind to illustrate the use of WebSocket technology. However, there are many other use cases, such as collaborative work on a text document online or online games like chess presented at the <a href="https://blogs.oracle.com/javaone/entry/the_javaone_2013_technical_keynote">JavaOne 2013 keynote</a>.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>This application is available on the Cloud thanks to <span class="line-through"><a href="https://www.openshift.com/">OpenShift</a>, the cloud computing PaaS product by RedHat. It&#8217;s deployed on WildFly 8.0.0-Beta1 (normaly certified Java EE 7 to the end of 2013). To set up an application server like WildFly on OpenShit, you just need to read <a href="https://www.openshift.com/blogs/deploy-websocket-web-applications-with-jboss-wildfly">this Shekhar Gulati&#8217;s blog post</a></span> <a href="https://www.clever-cloud.com">Clever Cloud</a> PaaS!</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_websocket_ws_a_new_protocol_different_from_http">WebSocket (WS): a new protocol different from HTTP</h2>
<div class="sectionbody">
<div class="paragraph">
<p><a href="http://tools.ietf.org/html/rfc2616">HTTP</a> is the standard protocol for the Web, it&#8217;s very effective for a lot of use cases but, nevertheless, has <strong>some drawbacks</strong> in the case of <strong>interactive Web applications</strong> :</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>half-duplex</strong>: based on the request/response pattern, the client sends a request and the server performs processing before sending a response, the client is forced to wait for a server response</p>
</li>
<li>
<p><strong>verbose</strong>: a lot of information are send in HTTP headers associated with the message, both in the HTTP request and in the HTTP response</p>
</li>
<li>
<p>in order to add a <strong>server push</strong> mode, you need to use workaround (polling, long polling, Comet/Ajax) since there is no standard</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>This protocol is not optimized to scale on large applications that have significant needs of real-time bi-directional communication. This is why the <strong>new WebSocket protocol</strong> offers more advanced features than HTTP because it is:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>based on <strong>1 unique TCP connection between 2 peers</strong> (whereas each HTTP request/response needs a new TCP connection)</p>
</li>
<li>
<p><strong>bidirectionnal</strong> : client can send message to server and server can also send message to client</p>
</li>
<li>
<p><strong>full-duplex</strong> : client can send multiple messages to server, as well as server to client without waiting for a response from each other</p>
</li>
</ul>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>'The term <strong>client</strong> is used only to define the one that initiate the connection. Once the connection is established, client and server become both <strong>peers</strong>, with the same capacity.'</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The WebSocket protocol was originally intended to be part of the HTML5 specification but as HTML5 will be officially released in 2014, the WebSocket protocol is finally set, as well as HTTP protocol, by an IETF specification, <a href="http://tools.ietf.org/html/rfc6455">with RFC 6455</a>.</p>
</div>
<div class="paragraph">
<p>As shown in the diagram below, the <strong>WebSocket protocol works in two phases</strong> named :</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><strong>handshake (open and close)</strong></p>
</li>
<li>
<p><strong>data transfer</strong></p>
</li>
</ol>
</div>
<div id="websocket_protocol" class="imageblock">
<div class="content">
<img src="https://mgreau.com/posts/images/WebSocket_Protocol.png" alt="Diagram which explain how does the WebSocket protocol work" width="550">
</div>
<div class="title">Figure 3. How does the WebSocket protocol work</div>
</div>
<div class="sect2">
<h3 id="_opening_handshake">Opening Handshake</h3>
<div class="paragraph">
<p>The <strong>Opening Handshake</strong> phase is a <strong>unique HTTP request/response</strong> between the one who initiate the connection (peer client) and the peer server. This HTTP exchange is specific because it uses the concept of <a href="http://tools.ietf.org/html/rfc2616#section-14.42"><strong>Upgrade, defined in the HTTP specification</strong></a>.
The principle is simple : <strong>Upgrade HTTP</strong> allows the client to ask the server to change the communication protocol and thus ensure that the client and server can discuss using a protocol other than HTTP.</p>
</div>
<div id="eg1-callouts" class="exampleblock">
<div class="title">Example 1. HTTP Handshake sample request</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">GET /usopen/matches/1234 HTTP/1.1     <i class="conum" data-value="1"></i><b>(1)</b>
Host: wildfly-mgreau.rhcloud.com:8000  <i class="conum" data-value="2"></i><b>(2)</b>
Upgrade: websocket  <i class="conum" data-value="3"></i><b>(3)</b>
Connection: Upgrade <i class="conum" data-value="4"></i><b>(4)</b>
Origin: http://wildfly-mgreau.rhcloud.com
Sec-WebSocket-Key:0EK7XmpTZL341oOh7x1cDw==
Sec-WebSocket-Version:13</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>HTTP GET method and HTTP 1.1 version required</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Host used for the WebSocket connection</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Request to upgrade to the WebSocket protocol</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Request to upgrade from HTTP to another protocol</td>
</tr>
</table>
</div>
</div>
</div>
<div id="eg2-callouts" class="exampleblock">
<div class="title">Example 2. HTTP Handshake Response sample</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">HTTP/1.1 101 Switching Protocols <i class="conum" data-value="1"></i><b>(1)</b>
Connection:Upgrade
Sec-WebSocket-Accept:SuQ5/hh0kStSr6oIzDG6gRfTx2I=
Upgrade:websocket <i class="conum" data-value="2"></i><b>(2)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>HTTP Response Code 101 : server is compatible and accept to send messages through another protocol</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Upgrade to the WebSocket protocol is accepted</td>
</tr>
</table>
</div>
</div>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<div class="paragraph">
<p>'When the upgrade request from HTTP to WebSocket protocol is approved by the endpoint server, it&#8217;s no longer possible to use HTTP communication, all exchanges have to be made through the WebSocket protocol.'</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_data_transfer">Data transfer</h3>
<div class="paragraph">
<p>Once the <strong>handshake</strong> is approved, the use of WebSocket protocol is established. There are an open connection on the 'peer server side' as well on the 'peer client side', callback handlers are called to initiate the communication.<br>
The <strong>Data transfer</strong> can now begin, so the 2 peers can exchange messages in a bidirectionnal and full-duplex communication.</p>
</div>
<div class="paragraph">
<p>As shown in the diagram named <strong>Figure 3</strong>, the peer server can send multiple messages ('in this example : 1 message for each scored point, 1 message each time any user bet on this game and 1 message at the end of the match') without any peer client response and the peer client can also send messages at any time ('in this example : betting on the winner of the match').
Each peer can send a specific message to close the connection.<br></p>
</div>
<div class="paragraph">
<p>With Java EE7 Platform, the peer server side code is written in <strong>Java</strong> while the peer client side code is in <strong>Java or Javascript</strong>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_closing_handshake">Closing Handshake</h3>
<div class="paragraph">
<p>This phase <strong>can be initiated by both peer</strong>. A peer that want to close the communication need to send a <strong>close control frame</strong> and it will received a close control frame too as a response.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_websocket_javascript_api_client">WebSocket Javascript API (Client)</h2>
<div class="sectionbody">
<div class="paragraph">
<p>To communicate from a Web application with a server using the WebSocket protocol, it&#8217;s necessary to use a <strong>client Javascript API</strong>. It&#8217;s the role of W3C to define this API.
The W3C specification for the <a href="http://w3.org/TR/websockets/">JavaScript WebSocket API</a> is being finalized. <a href="http://www.w3.org/TR/websockets/#websocket">The WebSocket interface</a> provides, among others, the following:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>an attribute to define the connection URL to the server Endpoint (url)</p>
</li>
<li>
<p>an attribute to know the status of the connection (readyState : CONNECTING, OPEN, CLOSING, CLOSED)</p>
</li>
<li>
<p>some <strong>Event Handler</strong> in connection with the WebSocket lifecycle, eg :</p>
<div class="ulist">
<ul>
<li>
<p>the Event Handler onopen is called when a new connection is open</p>
</li>
<li>
<p>the Event Handler onerror is called when an error occured during the communication</p>
</li>
<li>
<p>the Event Handler onmessage is called when a message arrives from the server</p>
</li>
</ul>
</div>
</li>
<li>
<p>methods (send(DOMString data), send(Blob data)) with which it&#8217;s possible to send different type of flow(text, binary) to the Endpoint server</p>
</li>
</ul>
</div>
<div id="eg3-callouts" class="exampleblock">
<div class="title">Example 3. Javascript source code example, from <a href="http://websocket.org" class="bare">http://websocket.org</a></div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-javascript" data-lang="javascript">var wsUri = "ws://echo.websocket.org/";

function testWebSocket() {

	websocket = new WebSocket(wsUri);
	websocket.onopen = function(evt) { onOpen(evt) };
	websocket.onclose = function(evt) { onClose(evt) };
	websocket.onmessage = function(evt) { onMessage(evt) };
	websocket.onerror = function(evt) { onError(evt) }; }
}

function onOpen(evt) {
	writeToScreen("CONNECTED");
	doSend("WebSocket rocks");
}
function onClose(evt) {
	writeToScreen("DISCONNECTED");
}
function onMessage(evt) {
	writeToScreen('&lt;span style="color: blue;"&gt;RESPONSE: ' + evt.data+'&lt;/span&gt;');
	websocket.close();
}

function onError(evt) {
	writeToScreen('&lt;span style="color: red;"&gt;ERROR:&lt;/span&gt; ' + evt.data);
}
function doSend(message) {
	writeToScreen("SENT: " + message);
	websocket.send(message);
}</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_jsr_386_java_api_for_websocket_protocol">JSR 386 : Java API for WebSocket protocol</h2>
<div class="sectionbody">
<div class="paragraph">
<p>As the W3C defines how to use WebSocket in Javascript, the <strong>Java Communitee Process (JCP)</strong> does the same for the Java world via the JSR 386.<br>
JSR 356 defines a <a href="http://jcp.org/en/jsr/detail?id=356">Java API for WebSocket protocol</a> which be part of <strong>Java EE Web Profile</strong> and give the ability to :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>create a *WebSocket Endpoint* (server or client), the name given to the Java component that can communicate via the WebSocket protocol</p>
</li>
<li>
<p>the choice of <strong>annotation</strong> or programmatic approach</p>
</li>
<li>
<p><strong>send and consume messages</strong> controls, text or binary via this protocol</p>
<div class="ulist">
<ul>
<li>
<p>manage the message as a complete message or a sequence of partial messages</p>
</li>
<li>
<p>send or receive messages as Java objects (concept of <strong>encoders / decoders</strong>)</p>
</li>
<li>
<p>send messages <strong>synchronously or asynchronously</strong></p>
</li>
</ul>
</div>
</li>
<li>
<p>configure and manage <strong>WebSocket Session</strong> (timeout, cookies&#8230;&#8203;)</p>
</li>
</ul>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
'The open source JSR-356 RI (Reference Implementation) is <a href="https://tyrus.java.net/">the project Tyrus</a>'
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="_websocket_server_endpoint">WebSocket Server Endpoint</h3>
<div class="paragraph">
<p>The transformation of a Plain Old Java Object (POJO) to a <strong>Server WebSocket Endpoint</strong> (namely capable of handling requests from different customers on the same URI) is <strong>very easy</strong> since you only have to annotate the Java Class with <strong>@ServerEndpoint</strong> and one method with <strong>@OnMessage</strong> :</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">import javax.websocket.OnMessage;
import javax.websocket.ServerEndpoint;

@ServerEndpoint("/echo") <i class="conum" data-value="1"></i><b>(1)</b>
public class EchoServer {

	@OnMessage <i class="conum" data-value="2"></i><b>(2)</b>
	public String handleMessage(String message){
		return "Thanks for the message: " + message;
	}

}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>@ServerEndpoint transforms this POJO into a WebSocket Endpoint, the <strong>value</strong> attribute is mandatory in order to set the access URI to this Endpoint</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>the 'handleMessage' method will be invoked for each received message</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_annotations">Annotations</h3>
<div class="paragraph">
<p>This Java API provides several types of annotations to be fully compatible with the WebSocket protocol :</p>
</div>
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 50%;">
<col style="width: 50%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Annotation</th>
<th class="tableblock halign-left valign-top">Role</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@ServerEndpoint</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Declare a Server Endpoint</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@ClientEndpoint</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Declare a Client Endpoint</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@OnOpen</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Declare this method handles open events</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@OnMessage</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Declare this method handles Websocket messages</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@OnError</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Declare this method handles error</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@OnClose</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Declare this method handles WebSocket close events</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>@ServerEndpoint attributes are listed below :</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">value</dt>
<dd>
<p>relative URI or template URI (ex: "/echo", "/matches/{match-id}")</p>
</dd>
<dt class="hdlist1">decoders</dt>
<dd>
<p>list of message decoder classnames</p>
</dd>
<dt class="hdlist1">encoders</dt>
<dd>
<p>liste of message encoder classnames</p>
</dd>
<dt class="hdlist1">subprotocols</dt>
<dd>
<p>list of the names of the supported subprotocols (ex: <a href="http://wamp.ws" class="bare">http://wamp.ws</a>)</p>
</dd>
</dl>
</div>
</div>
<div class="sect2">
<h3 id="_encoders_and_decoders">Encoders and Decoders</h3>
<div class="paragraph">
<p>As described earlier in this article, the Endpoint server can receive different types of content in messages : data in text format (JSON, XML &#8230;&#8203;) or binary format.<br>
To effectively manage the messages from 'peers client' or to them in the application business code, it is possible to create <strong>Encoders and Decoders</strong> Java classes.</p>
</div>
<div class="paragraph">
<p>Whatever the transformation algorithm, it will then be possible to transform  :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>the business POJO to flow in the desired format for communication (JSON, XML, Binary &#8230;&#8203;)</p>
</li>
<li>
<p>inflows in specific format(JSON, XML..) to the business POJO</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Thus, the application code is structured so that the business logic is not affected by the type and format of messages exchanged between the 'peer server' and 'peers client' flows.</p>
</div>
<div class="paragraph">
<p>A concrete example is presented later in the article.</p>
</div>
</div>
<div class="sect2">
<h3 id="_websocket_client_endpoint">WebSocket Client Endpoint</h3>
<div class="paragraph">
<p>This Java API also offers support for creating client-side Java Endpoints.</p>
</div>
<div id="eg4-callouts" class="exampleblock">
<div class="title">Example 4. Java Client Endpoint sample</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">@ClientEndpoint
public class HelloClient {

	@OnMessage
	public String message(String message){
		// code
	}
}

WebSocketContainer c = ContainerProvider.getWebSocketContainer();
c.connectToServer(HelloClient.class, "hello");</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_us_open_application">US OPEN Application</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The sample application is deployed as a WAR outcome of a build with Apache Maven.
In addition to the traditional management WebSocket lifecycle, the sending messages workflow is as follows :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>each 'peer client' can connect to 1 or 4 live matches</p>
</li>
<li>
<p>each 'peer client' can disconnect from a match</p>
</li>
<li>
<p>at each point of a match, clients which are connected to this match will receive datas (score, service&#8230;&#8203;)</p>
</li>
<li>
<p>the 'peer client' may send a message to bet on the winner of the match</p>
</li>
<li>
<p>each time that one 'peer client' bet on a match, all others 'peers clients' which have bet on the same match, will receive a message with the total number of bettors</p>
</li>
<li>
<p>at the end of the match, 'peers client' receive a message containing the name of the winner and a specific message if they bet on this match</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>All messages are exchanged in JSON format</strong><br></p>
</div>
<div class="paragraph">
<p>The project layout is as follows :</p>
</div>
<div id="eg5-callouts" class="exampleblock">
<div class="title">Example 5. Maven project structure</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">+ src/main/java
   |+ com.mgreau.wildfly.websocket
      |+ decoders
         |- MessageDecoder.java   <i class="conum" data-value="1"></i><b>(1)</b>
      |+ encoders       <i class="conum" data-value="2"></i><b>(2)</b>
         |- BetMessageEncoder.java
         |- MatchMessageEncoder.java
      |+ messages       <i class="conum" data-value="3"></i><b>(3)</b>
         |- BetMessage.java
         |- MatchMessage.java
         |- Message.java
      |+ rest       <i class="conum" data-value="4"></i><b>(4)</b>
         |- RestApplication.java
         |- TournamentREST.java
      |- MatchEndpoint.java    <i class="conum" data-value="5"></i><b>(5)</b>
      |- StarterService.java   <i class="conum" data-value="6"></i><b>(6)</b>
      |- TennisMatch.java      <i class="conum" data-value="7"></i><b>(7)</b>
+ src/main/resources
+ scr/main/webapp
   |+ css
   |+ images
   |+ js
      |+ live    <i class="conum" data-value="8"></i><b>(8)</b>
         |- app.js
         |- controllers.js
         |- directives.js
         |- services.js
      |- websocket.js  <i class="conum" data-value="9"></i><b>(9)</b>
   |+ templates
      |- bet.html
      |- match.html
      |- msg.html
   |- index.html
   |- live.html
pom.xml</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Decode JSON messages sent from the 'peer client' (about bet on the winner) to a POJO ('BetMessage')</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Encode in JSON format (via JSON-P), all messages about the winner and the match details for 'peers clients'</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>POJOs to handle messages sent between peers</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>REST endpoint to list all tournament&#8217;s matches</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>The application WebSocket Server Endpoint ('peer server')</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>EJB @Startup in order to initialize this application at deployment time</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>POJO to handle informations about the match</td>
</tr>
<tr>
<td><i class="conum" data-value="8"></i><b>8</b></td>
<td>AngularJS files to handle several matches (live.html) with REST and WebSocket calls</td>
</tr>
<tr>
<td><i class="conum" data-value="9"></i><b>9</b></td>
<td>File containing the implementation of Javascript API for WebSocket protocol to handle the client side of the communication for the simple case (index.html)</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_maven_dependencies_for_java_ee_7_api">Maven dependencies for Java EE 7 API</h3>
<div id="eg6-callouts" class="exampleblock">
<div class="title">Example 6. pom.xml with Java EE 7 dependencies</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;project&gt;
...
&lt;properties&gt;
	&lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;
	&lt;!-- Java EE 7 --&gt;
	&lt;javaee.api.version&gt;7.0&lt;/javaee.api.version&gt;
&lt;/properties

&lt;dependencies&gt;
	&lt;dependency&gt;
		&lt;groupId&gt;javax&lt;/groupId&gt; <i class="conum" data-value="1"></i><b>(1)</b>
		&lt;artifactId&gt;javaee-api&lt;/artifactId&gt;
		&lt;version&gt;${javaee.api.version}&lt;/version&gt;
		&lt;scope&gt;provided&lt;/scope&gt;
	&lt;/dependency&gt;
&lt;/dependencies&gt;
...
&lt;/project&gt;</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>It&#8217;s important to use the Java EE 7 dependencies to be able to deploy the same application in multiple Java EE application servers (WildFly, Glassfish&#8230;&#8203;) <strong>without changing code</strong>.</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_add_server_endpoint">Add Server Endpoint</h3>
<div class="paragraph">
<p>This endpoint can receive messages about betting on the winner of a match (identified by match-id) and it can also send to 'peers client' all informations about the course of the match and bets.</p>
</div>
<div id="eg7-callouts" class="exampleblock">
<div class="title">Example 7. Server Endpoint : MatchEndpoint.java</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">@ServerEndpoint(
		value = "/matches/{match-id}",  <i class="conum" data-value="1"></i><b>(1)</b>
		        decoders = { MessageDecoder.class }, <i class="conum" data-value="2"></i><b>(2)</b>
		        encoders = { MatchMessageEncoder.class, BetMessageEncoder.class } <i class="conum" data-value="3"></i><b>(3)</b>
		)
public class MatchEndpoint {

	private static final Logger logger = Logger.getLogger("MatchEndpoint");

 	/** All open WebSocket sessions */
	static Set&lt;Session&gt; peers = Collections.synchronizedSet(new HashSet&lt;Session&gt;());

	/** Handle number of bets by match */
	static Map&lt;String, AtomicInteger&gt; nbBetsByMatch = new ConcurrentHashMap&lt;&gt;();

	@Inject StarterService ejbService;

	@OnOpen
	public void openConnection(Session session,
					@PathParam("match-id") String matchId) { <i class="conum" data-value="4"></i><b>(4)</b>
    	session.getUserProperties().put(matchId, true);
    	peers.add(session);

    	//Send live result for this match
    	send(new MatchMessage(ejbService.getMatches().get(matchId)), matchId);
	}

	public static void send(MatchMessage msg, String matchId) {
	  try {
	    /* Send updates to all open WebSocket sessions for this match */
	    for (Session session : queue) {
    	  if (Boolean.TRUE.equals(session.getUserProperties().get(matchId))){
	        if (session.isOpen()){
		      session.getBasicRemote().sendObject(msg);	<i class="conum" data-value="5"></i><b>(5)</b>
	        }
    	  }
	    }
	  } catch (IOException | EncodeException e) {
	    logger.log(Level.INFO, e.toString());
	  }
	}

	public static void sendBetMessage(Session session, BetMessage betMsg, String matchId)
	{
	  try {
	      betMsg.setNbBets(nbBetsByMatch.get(matchId).get());
	      session.getBasicRemote().sendObject(betMsg);
		  logger.log(Level.INFO, "BetMsg Sent: {0}", betMsg.toString());
	  } catch (IOException | EncodeException e) {
		  logger.log(Level.SEVERE, e.toString());
	  }
	}

	@OnMessage
	public void message(final Session session, BetMessage msg,    <i class="conum" data-value="6"></i><b>(6)</b>
					@PathParam("match-id") String matchId) {
	  session.getUserProperties().put("bet", msg.getWinner());

	  //Send betMsg with bet count
	  if (!nbBetsByMatch.containsKey(matchId)){
	     nbBetsByMatch.put(matchId, new AtomicInteger());
	  }
	  nbBetsByMatch.get(matchId).incrementAndGet();
	  sendBetMessages(null, matchId, false);
	}

	@OnClose
	public void closedConnection(Session session,
	                    @PathParam("match-id") String matchId) {
	  if (session.getUserProperties().containsKey("bet")){
		  nbBetsByMatch.get(matchId).decrementAndGet();
		  sendBetMessages(null, matchId, false);
	  }
	  /* Remove this connection from the queue */
	  peers.remove(session);
	}
...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Access URI to this Endpoint, as the application context-root is '/usopen', the final URL looks like this : ws://&lt;host&gt;:&lt;port&gt;/usopen/matches/1234</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>'MessageDecoder' transforms the incoming JSON flow (about the bet on the winner) into a POJO 'BetMessage'</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>This 2 encoders add the ability to transform from 'MatchMessage' POJO and 'BetMessage' POJO to messages in JSON format</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>@PathParam annotation allows to extract part of the WebSocket request and pass the value (id match) as the parameter of the method, it is possible to manage several match with multiple clients for each match.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Send, to connected peers, messages about the course of the match. Thanks to the 'MatchMessageEncoder' object, simply pass the 'MatchMessage' object.</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>Handle received messages about the bet on the winner, thanks to the 'MessageDecoder' object, one of the parameters of this method is a 'BetMessage' object</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_encodes_and_decodes_messages">Encodes and Decodes messages</h3>
<div class="paragraph">
<p>To encode or decode messages exchanged between peers, simply implement the appropriate interface according to the message type (text, binary) and direction of processing (encoding, decoding), then redefine the associated method.<br>
In the example below, it&#8217;s the <strong>encoder</strong> for MatchMessage POJO to JSON format. The API used to perform this treatment is also a new API released with Java EE 7 : <a href="http://jcp.org/en/jsr/detail?id=353">Java API for JSON Processiong (JSON-P)</a></p>
</div>
<div id="eg8-callouts" class="exampleblock">
<div class="title">Example 8. Text Encoder : MatchMessageEncoder.java</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">public class MatchMessageEncoder implements Encoder.Text&lt;MatchMessage&gt; {

	@Override
	public String encode(MatchMessage m) throws EncodeException {
		StringWriter swriter = new StringWriter();
		try (JsonWriter jsonWrite = Json.createWriter(swriter)) {
			JsonObjectBuilder builder = Json.createObjectBuilder();
			builder.add(
				"match",
				Json.createObjectBuilder()
					.add("serve", m.getMatch().getServe())
					.add("title", m.getMatch().getTitle())
					...
			}

			jsonWrite.writeObject(builder.build());
		}
		return swriter.toString();
	}
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_html5_web_client_single_match_index_html">HTML5 Web Client (single match - index.html)</h3>
<div class="paragraph">
<p>The index.html page of this application loads the <strong>websocket.js</strong> file to implement the Javascript WebSocket API and thus interact with the Java Server Endpoint. This page handle only one single match.</p>
</div>
<div id="eg9-callouts" class="exampleblock">
<div class="title">Example 9. API Javascript implemented into websocket.js</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight nowrap"><code class="language-javascript" data-lang="javascript">var wsUrl;
if (window.location.protocol == 'https:') {  <i class="conum" data-value="1"></i><b>(1)</b>
	wsUrl = 'wss://' + window.location.host + ':8443/usopen/matches/1234';
} else {
	wsUrl = 'ws://' + window.location.host + ':8000/usopen/matches/1234';
}

function createWebSocket(host) {
	if (!window.WebSocket) {    <i class="conum" data-value="2"></i><b>(2)</b>
	...
	} else {
		socket = new WebSocket(host);   <i class="conum" data-value="3"></i><b>(3)</b>
		socket.onopen = function() {
			document.getElementById("m1-status").innerHTML = 'CONNECTED...';
		};
		socket.onclose = function() {
			document.getElementById("m1-status").innerHTML = 'FINISHED';
		};
		...
		socket.onmessage = function(msg) {
			try {
				console.log(data);
				var obj = JSON.parse(msg.data);     <i class="conum" data-value="4"></i><b>(4)</b>
				if (obj.hasOwnProperty("match")){   <i class="conum" data-value="5"></i><b>(5)</b>
					//title
					m1title.innerHTML = obj.match.title;
					// comments
					m1comments.value = obj.match.comments;
					// serve
					if (obj.match.serve === "player1") {
						m1p1serve.innerHTML = "S";
						m1p2serve.innerHTML = "";
					} else {
						m1p1serve.innerHTML = "";
						m1p2serve.innerHTML = "S";
					}
					..
				}
				...
			} catch (exception) {
				data = msg.data;
				console.log(data);
			}
		}
	}
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Choose the appropriate WebSocket protocol according to the HTTP protocol currently used (secure or not)</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Check if the browser supports WebSocket API</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Create the WebSocket object</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Try to parse the JSON message sent by 'peer server', into the function called by onmessage Event Handler</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Check the received object type (MatchMessage or BetMessage) to achieve adequate treatment with DOM</td>
</tr>
</table>
</div>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>To find out which browsers are compatible with <strong>WebSocket API</strong> <a href="http://caniuse.com/#search=websocket">visit the website caniuse.com</a>. Today, the latest versions of browsers are compatible excepted for Android and Opera Mini Browser, which represent, both together, only 3% of web traffic.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_angularjs_client_several_matches_live_html">AngularJS Client (several matches - live.html)</h3>
<div class="paragraph">
<p>As we saw on the beginning of this post, the version to handle severals matches is developed with AngularJS on the client side.
So there are 4 JS files which contains :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>app.js : just define the 'tennisApp' angular application</p>
</li>
<li>
<p>controllers.js : the TournamentCtrl controller</p>
</li>
<li>
<p>directives.js :</p>
<div class="ulist">
<ul>
<li>
<p>bet directive and bet.html template to show the number of bettors and the current bet</p>
</li>
<li>
<p>match directive and match.html template to handle live informations</p>
</li>
<li>
<p>msg directive and msg.html template to shwo a message at the end of the match with the bet result if needed</p>
</li>
</ul>
</div>
</li>
<li>
<p>services.js :</p>
<div class="ulist">
<ul>
<li>
<p>WebSocketService : to handle WebSocket lifecycle with callback</p>
</li>
<li>
<p>TournamentRESTService : to get all matches from REST Endpoint server</p>
</li>
<li>
<p>BetsService and MatchesServices</p>
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>'This post is not a tutorial about AngularJS since it&#8217;s my first app with this framework. But it was a quick solution to handle several matches on the client side. You can read the 3 following posts to better understand the JS code : <a href="http://www.frangular.com/2013/10/scope-isole-dans-les-directives.html">directives in French</a> and <a href="http://suhairhassan.com/2013/10/21/refactoring-to-angularjs-directive.html#.Un1t12S1Xv2">Refactoring to AngularJS directives</a> and <a href="http://clintberry.com/2013/angular-js-websocket-service/">AngularJS WebSocket Service example</a>'</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_source_code_on_github">Source code on Github</h3>
<div class="paragraph">
<p>You can <strong>fork this project on Github</strong> at *https://github.com/mgreau/javaee7-websocket*</p>
</div>
<div class="paragraph">
<p>This sample application is basic, there could be many improvements like betting on other criteria&#8230;&#8203;</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>'A feature that could be interesting technically, would be to create a new type of <strong>bet based on the coordinates of each winning point</strong>. Simply draw the ground through the HTML5 Canvas API and manage the coordinates selected by the user (such as winning point) and then compare with the actual coordinates at a point winner. '</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_build_and_deploy_the_war">Build and Deploy the WAR</h3>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Prerequisite :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>JDK 7</p>
</li>
<li>
<p>Apache Maven 3.0.4+</p>
</li>
<li>
<p>Java EE 7 Application Server (WildFly 8)</p>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>In order to build the WAR, you just have to execute the Maven command below ;</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">mvn clean package</code></pre>
</div>
</div>
<div class="paragraph">
<p>If your application server is WildFly, you can quickly deploy the WAR with the command below (WildFly has to be started) :</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">mvn jboss-as:deploy</code></pre>
</div>
</div>
<div class="paragraph">
<p>The usopen application is then available at :</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="http://localhost:8080/usopen/" class="bare">http://localhost:8080/usopen/</a> for the simple case with only native Javascript</p>
</li>
<li>
<p><a href="http://localhost:8080/usopen/matches" class="bare">http://localhost:8080/usopen/matches</a> for the version with severals matches and AngularJS</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>WildFly 10 uses its new *Web Server called http://undertow.io[Undertow]* which replaces Tomcat.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>'I didn&#8217;t test this application on Glassfish 4, but since I&#8217;m using only Java EE 7 API dependencies, it could work with the same code without problem. Let me know if you have troubles with GlassFish. '</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_benchmark_websocket_vs_rest">Benchmark : WebSocket VS REST</h2>
<div class="sectionbody">
<div class="paragraph">
<p>In order to have some metrics about the performance of this new protocol, Arun Gupta has developed <a href="https://github.com/arun-gupta/javaee7-samples/tree/master/websocket/websocket-vs-rest">an application that allows compare the execution time of</a> the same treatment performed by WebSocket code and REST code.</p>
</div>
<div class="paragraph">
<p>Each endpoint (REST Endpoint and WebSocket Endpoint) just do an "echo" so they only return the flows they receive. The web interface of the application allows you to define the size of the message and the number of times that the message must be sent before the end of the test.</p>
</div>
<div class="paragraph">
<p>The benchmark results, shown below, are quite eloquent :</p>
</div>
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Request</th>
<th class="tableblock halign-left valign-top">Total execution time<br>
<strong>REST Endpoint</strong></th>
<th class="tableblock halign-left valign-top">Total execution time<br>
<strong>WebSocket Endpoint</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Sending 10 messages of 1 byte</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">220 ms (63 ms)*</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">7 ms (29ms)*</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Sending 100 messages of 10 bytes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">986 ms (587 ms)*</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">57 ms (74 ms)*</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Sending 1000 messages of 100 bytes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">10 210 ms (4 636 ms)*</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">179 ms (288 ms)*</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Sending 5000 messages of 1000 bytes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">54 449 ms (18 049 ms)*</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1202 ms (2862 ms)*</p></td>
</tr>
</tbody>
</table>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>'Values between ()* represent the same tests on my laptop (MacBook Pro 2013 i5 - 8Go - 128SSD) with WildFly 8.0.0-beta1 with default configuration.'</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_references_about_websocket">References about WebSocket</h2>
<div class="sectionbody">
<div class="paragraph">
<p>I would particularly recommend <a href="https://twitter.com/arungupta">Arun Gupta</a>'s conferences, which allow you in less than 1 hour to discover and understand the WebSocket technology in general and the Java API for WebSocket.<br>
For more advanced information, the ideal is IETF, W3C and Java specifications.</p>
</div>
<div class="ulist bibliography">
<ul class="bibliography">
<li>
<p><a href="http://tools.ietf.org/html/rfc6455">RFC 6455: The WebSocket Protocol</a> - 'IETF Specification'</p>
</li>
<li>
<p><a href="http://w3.org/TR/websockets/">W3C: The WebSocket API</a> - 'W3C Specification' (Candidate Recommandation)</p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=356">JSR 356: Java API for WebSocket Protocol</a> - 'Java Specification'</p>
</li>
<li>
<p><a href="https://glassfish.java.net/adoptajsr/jsr356.html">Adopt a JSR - JSR 356</a></p>
</li>
<li>
<p><a href="http://www.youtube.com/watch?v=QqbuDFIT5To">Java EE 7 &amp; WebSocket API</a> - 'Arun Gupta&#8217;s conference @ SF' (from the 46th minute)</p>
</li>
<li>
<p><a href="http://www.parleys.com/play/51c1cceae4b0ed8770356828/chapter4/about">Getting Started with WebSocket and SSE</a> - 'Arun Gupta&#8217;s conference @ Devoxx UK 2013'</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>'This article was structured based on the UK 2013 Devoxx conference.'</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This article has introduced, through a concrete example, <strong>the WebSocket protocol, the HTML5 WebSocket API and Java API for WebSocket released with Java EE 7</strong>. It was already possible to use WebSocket with Java frameworks like <a href="http://async-io.org/download.html">Atmosphere</a> but lacked a standard.<br>
Today all <strong>standards are completed or about to be</strong>, this new technology meets a specific need and is promising in terms of performance. To be heavily used, this protocol will need to be allowed in businesses where often only the HTTP protocol is permitted.</p>
</div>
</div>
</div>]]></description><link>https://mgreau.com/posts/2013/11/11/javaee7-websocket-angularjs-wildfly.html</link><guid isPermaLink="true">https://mgreau.com/posts/2013/11/11/javaee7-websocket-angularjs-wildfly.html</guid><category><![CDATA[Java EE]]></category><category><![CDATA[ WebSocket]]></category><category><![CDATA[ WildFly]]></category><category><![CDATA[ angularJS]]></category><dc:creator><![CDATA[Maxime Gréau]]></dc:creator><pubDate>Mon, 11 Nov 2013 00:00:00 GMT</pubDate></item><item><title><![CDATA[Java EE 7 et l'API Java pour WebSocket (JSR 356)]]></title><description><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Cet article présente et met en oeuvre à travers un exemple concret et <a href="http://wildfly-mgreau.rhcloud.com/usopen/">disponible en ligne</a>, une des 4 nouvelles JSRs de <strong><a href="http://jcp.org/en/jsr/detail?id=342">Java EE 7</a></strong>, à savoir <strong><a href="http://jcp.org/en/jsr/detail?id=356">l&#8217;API Java pour communiquer via le protocole WebSocket (JSR 356)</a></strong>, .
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Après la lecture de cet article, vous devriez être en mesure de comprendre la définition de ce qu&#8217;il est possible de faire avec le protocole WebSocket, donnée lors de Devoox UK par <a href="https://twitter.com/arungupta">Arun Gupta</a> :</p>
</div>
<div id="think" class="listingblock">
<div class="content">
<pre>WebSocket gives you bidirectionnal, full duplex, communication channel over a single TCP.</pre>
</div>
</div>
</div>
<div id="toc" class="toc">
<div id="toctitle">Table des matières de l'article</div>
<ul class="sectlevel1">
<li><a href="#_introduction_java_ee_7">Introduction à Java EE 7</a></li>
<li><a href="#_demo_application_html5_jsr_356_api_d_ploy_e_sur_wildfly_8_openshift">DEMO : Application HTML5 / JSR-356 API déployée sur Wildfly 8 (OpenShift)</a></li>
<li><a href="#_websocket_ws_un_nouveau_protocole_diff_rent_de_http">WebSocket (WS) : un nouveau protocole différent de HTTP</a>
<ul class="sectlevel2">
<li><a href="#_handshake">Handshake</a></li>
<li><a href="#_data_transfer">Data transfer</a></li>
</ul>
</li>
<li><a href="#_websocket_javascript_api_client">WebSocket Javascript API (Client)</a></li>
<li><a href="#_jsr_386_java_api_pour_websocket">JSR 386 : Java API pour WebSocket</a>
<ul class="sectlevel2">
<li><a href="#_websocket_endpoint_serveur">WebSocket Endpoint : Serveur</a></li>
<li><a href="#_annotations">Annotations</a></li>
<li><a href="#_encoders_et_decoders">Encoders et Decoders</a></li>
<li><a href="#_websocket_endpoint_client">WebSocket Endpoint : Client</a></li>
</ul>
</li>
<li><a href="#_application_us_open">Application US OPEN</a>
<ul class="sectlevel2">
<li><a href="#_d_pendances_maven_java_ee_7">Dépendances Maven Java EE 7</a></li>
<li><a href="#_cr_er_le_server_endpoint">Créer le Server Endpoint</a></li>
<li><a href="#_encoder_et_d_coder_les_messages_chang_s">Encoder et Décoder les messages échangés</a></li>
<li><a href="#_client_web_html5">Client Web HTML5</a></li>
<li><a href="#_sources_de_l_exemple_sur_github">Sources de l&#8217;exemple sur Github</a></li>
<li><a href="#_construire_et_d_ployer_le_war">Construire et Déployer le WAR</a></li>
</ul>
</li>
<li><a href="#_performances_websocket_vs_rest">Performances : WebSocket vs REST</a></li>
<li><a href="#_r_f_rences_pour_tout_savoir_sur_les_websocket">Références pour tout savoir sur les WebSocket</a></li>
<li><a href="#_conclusion">Conclusion</a></li>
</ul>
</div>
</div>
<div class="sect1">
<h2 id="_introduction_java_ee_7">Introduction à Java EE 7</h2>
<div class="sectionbody">
<div class="paragraph">
<p>La <strong>Plateforme Java Entreprise Edition</strong> est sortie en version 7 (Java EE 7) au mois de Juin 2013.
Dans la continuité des versions Java EE 5 et Java EE 6, <strong>Java EE 7</strong> propose toujours de simplifier le travail du développeur.
Cette version agrémente les versions précédentes avec 3 objectifs principaux :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>s&#8217;interfacer avec <strong>HTML5</strong> (WebSocket API, JSON-P API, JAX-RS)</p>
</li>
<li>
<p>avoir une <strong>meilleure productivité</strong> (nouvelles annotations, moins de code inutile, meilleure cohésion entre JSRs)</p>
</li>
<li>
<p>répondre aux <strong>besoins des entreprises</strong> (Batch API, JMS API 2.0)</p>
</li>
</ul>
</div>
<div id="javaee7_intro" class="imageblock">
<div class="content">
<img src="https://mgreau.com/posts/images/posts/javaee7_intro.png" alt="Java EE 7 - Objectifs">
</div>
<div class="title">Figure 1. Les 3 objectifs de Java EE 7</div>
</div>
<div class="paragraph">
<p>Java Platform, Entreprise Edition 7 (JSR 342), se résume donc autour de :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>4 nouvelles spécifications : <strong>Java API for WebSocket 1.0</strong>, <strong>Java API for JSON Processing 1.0</strong> , <strong>Batch Applications 1.0</strong> et <strong>Concurrency Utilities for Java EE 1.0</strong></p>
</li>
<li>
<p>3 spécifications avec une mise à jour majeure : <strong>JMS 2.0</strong>, <strong>JAX-RS 2.0</strong> et <strong>EL 3.0</strong></p>
</li>
<li>
<p>ainsi que 7 spécifications mises à jour dans une version mineure : <strong>JPA 2.1</strong>, <strong>Servlet 3.1</strong>, <strong>EJB 3.2</strong>, <strong>CDI 1.1</strong>, <strong>JSF 2.2</strong> et <strong>Bean Validation 1.1</strong></p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_demo_application_html5_jsr_356_api_d_ploy_e_sur_wildfly_8_openshift">DEMO : Application HTML5 / JSR-356 API déployée sur Wildfly 8 (OpenShift)</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Les plus impatients peuvent accéder à la {link-demo}[démonstration en ligne] du code qui va être, en partie, expliqué dans cet article.
Il s&#8217;agit d&#8217;une application qui permet :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>de suivre un match de Tennis en Live (Finale de l&#8217;US Open 20013) sans aucune action autre que la connexion à l&#8217;URL</p>
</li>
<li>
<p>de parier sur le vainqueur du match</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Vous allez me dire : "Rien d&#8217;extraordinaire !", et vous aurez raison.</p>
</div>
<div class="paragraph">
<p>A première vue, ce sont des choses que nous connaissons déjà sur beaucoup d&#8217;applications aujourd&#8217;hui, mais celle-ci est intéressante techniquement car comme vous le verrez au cours de l&#8217;article, tout est basé sur du <strong>standard autour du nouveau protocole WebSocket (ws:// ou wss://)</strong> et non sur du "hacking" de protocole HTTP.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="title">Les technologies utilisées pour le developpement de cette application sont :</div>
<div class="ulist">
<ul>
<li>
<p>côté client : HTML5, CSS, Javascript (WebSocket API) avec 'Bootstrap CSS mais sans JQuery ou BootstrapJS'</p>
</li>
<li>
<p>côte serveur : Java API for WebSocket, EJB, JSON-P</p>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div>
<div id="websocket_example" class="imageblock">
<div class="content">
<a class="image" href="http://wildfly-mgreau.rhcloud.com/usopen/"><img src="https://mgreau.com/posts/images/posts/websocket_example.png" alt="Mise en oeuvre des WebSocket (Java API et Javascript API)"></a>
</div>
<div class="title">Figure 2. Mise en oeuvre des WebSocket (Java API et Javascript API)</div>
</div>
<div class="paragraph">
<p>Non cette démonstration n&#8217;est <strong>pas une application de chat :)</strong>
Il est évident que la démo "chat" est celle qui vient en premier à l&#8217;esprit pour illustrer l&#8217;utilisation de la technologie WebSocket. Néanmoins, il existe beaucoup d&#8217;autres cas d&#8217;utilisation, comme par exemple le travail collaboratif sur un document texte en ligne. Ou encore les jeux en ligne comme le jeu d'échec présenté lors de la <a href="https://blogs.oracle.com/javaone/entry/the_javaone_2013_technical_keynote">keynote de JavaOne 2013</a>.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>'Cette application est disponible sur le Cloud grâce à <a href="https://www.openshift.com/">OpenShift</a>, la solution Cloud de RedHat. Elle est déployée sur le serveur d&#8217;applications Wildfly 8.0.0-Beta3 (normalement certifié Java EE 7 fin 2013). Pour mettre en place un serveur de ce type, il suffit de suivre <a href="https://www.openshift.com/blogs/deploy-websocket-web-applications-with-jboss-wildfly">le post de Shekhar Gulati</a>'</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_websocket_ws_un_nouveau_protocole_diff_rent_de_http">WebSocket (WS) : un nouveau protocole différent de HTTP</h2>
<div class="sectionbody">
<div class="paragraph">
<p><a href="http://tools.ietf.org/html/rfc2616">HTTP</a> est le protocole standard utilisé pour le Web, il est très efficace pour certains cas d&#8217;utilisation mais il dispose néanmoins de <strong>quelques inconvénients</strong> dans le cas <strong>d&#8217;applications Web intéractives</strong> :</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>half-duplex</strong> : basé sur le pattern request/response, le client envoi une requête puis le serveur réalise un traitement avant de renvoyer une réponse, le client est donc contraint d&#8217;attendre une réponse du serveur</p>
</li>
<li>
<p><strong>verbose</strong> : beaucoup d&#8217;informations sont présentes avec les headers HTTP associés au message, aussi bien dans la requête HTTP que dans la réponse HTTP</p>
</li>
<li>
<p>pour faire du <strong>server push</strong>, il est nécessaire d&#8217;utiliser des méthodes de contournement (polling, long polling, Comet/Ajax) car il n&#8217;existe pas de standard.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Ce protocole n&#8217;est donc pas optimisé pour scaler sur des applications qui ont d&#8217;important besoins de communication temps réel bi-directionnelle. C&#8217;est pourquoi le <strong>nouveau protocole WebSocket</strong> propose des fonctionnalités plus évoluées que HTTP, puisqu&#8217;il est :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>basé sur <strong>1 unique connexion TCP entre 2 peers</strong> (en HTTP chaque requête/réponse necessite une nouvelle connexion TCP)</p>
</li>
<li>
<p><strong>bi-directionnel</strong> : le client peut envoyer un message au serveur et le serveur peut envoyer un message au client</p>
</li>
<li>
<p><strong>full-duplex</strong> : le client peut envoyer plusieurs messages vers le serveur et le serveur vers le client sans attendre de réponse l&#8217;un de l&#8217;autre</p>
</li>
</ul>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>'Le terme <strong>client</strong> est utilisé uniquement pour définir celui qui va initialiser la connexion. Dès lors que la connexion est établie, le client et le serveur deviennent tous les deux des <strong>peers</strong>, avec les mêmes pouvoirs l&#8217;un par rapport à l&#8217;autre.'</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Le protocole WebSocket devait à l&#8217;origine faire partie de la spécification HTML5 mais comme celle-ci sortira officiellement en 2014, il est finalement défini, au même titre que HTTP, par une spécification IETF, <a href="http://tools.ietf.org/html/rfc6455">la RFC 6455</a>.</p>
</div>
<div class="paragraph">
<p>Comme le montre le schéma ci-après, le <strong>protocole WebSocket fonctionne en 2 phases</strong> nommées :</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><strong>handshake</strong></p>
</li>
<li>
<p><strong>data transfer</strong></p>
</li>
</ol>
</div>
<div id="websocket_protocol" class="imageblock">
<div class="content">
<img src="https://mgreau.com/posts/images/posts/WebSocket_Protocol.png" alt="Schéma d'explications du protocole WebSocket" width="550">
</div>
<div class="title">Figure 3. Explication du protocole WebSocket</div>
</div>
<div class="sect2">
<h3 id="_handshake">Handshake</h3>
<div class="paragraph">
<p>La phase nommée <strong>Handshake</strong> correspond à un <strong>unique échange requête/réponse HTTP</strong> entre l&#8217;initiateur de la connexion (peer client)  et le peer serveur. Cet échange HTTP est spécifique car il utilise la notion <a href="http://tools.ietf.org/html/rfc2616#section-14.42"><strong>d&#8217;Upgrade, définie dans la spécification HTTP</strong>.</a><br>
Le principe est simple : <strong>l&#8217;Upgrade HTTP</strong> permet au client de communiquer avec le serveur pour lui demander de changer de protocole de communication et ainsi faire en sorte que le client et le serveur utilisent un protocole autre que HTTP pour discuter.</p>
</div>
<div id="eg1-callouts" class="exampleblock">
<div class="title">Example 1. Exemple de Requête HTTP Handshake</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">GET /usopen/matches/1234 HTTP/1.1     <i class="conum" data-value="1"></i><b>(1)</b>
Host: wildfly-mgreau.rhcloud.com:8000  <i class="conum" data-value="2"></i><b>(2)</b>
Upgrade: websocket  <i class="conum" data-value="3"></i><b>(3)</b>
Connection: Upgrade <i class="conum" data-value="4"></i><b>(4)</b>
Origin: http://wildfly-mgreau.rhcloud.com
Sec-WebSocket-Key:0EK7XmpTZL341oOh7x1cDw==
Sec-WebSocket-Version:13</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Methode HTTP GET et version 1.1 obligatoires</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Host utilisé pour la connexion WebSocket</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Demande d&#8217;Upgrade vers le protocole WebSocket</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Demande d&#8217;Upgrade HTTP pour changer de protocole</td>
</tr>
</table>
</div>
</div>
</div>
<div id="eg2-callouts" class="exampleblock">
<div class="title">Example 2. Exemple de Réponse HTTP Handshake</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">HTTP/1.1 101 Switching Protocols <i class="conum" data-value="1"></i><b>(1)</b>
Connection:Upgrade
Sec-WebSocket-Accept:SuQ5/hh0kStSr6oIzDG6gRfTx2I=
Upgrade:websocket <i class="conum" data-value="2"></i><b>(2)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Code HTTP 101, le serveur est compatible et accepte le changement de protocole</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>L&#8217;upgrade vers le protocole WebSocket est accepté</td>
</tr>
</table>
</div>
</div>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<div class="paragraph">
<p>'Lorsque la demande d&#8217;upgrade du protocole HTTP vers le protocole Web Socket a été validée par le serveur endpoint, il n&#8217;y a plus de communication possible en HTTP, tous les échanges sont réalisés via le protocole WebSocket.'</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_data_transfer">Data transfer</h3>
<div class="paragraph">
<p>Une fois que le <strong>handshake</strong> est acceptée, la mise en place du protocole WebSocket est donc acquise. Une connexion côté 'peer server' est ouverte ainsi que côté 'peer client', une gestion de callback est activée pour initier la communication.<br>
La phase de <strong>Data transfer</strong> peut alors entrer en jeu, c&#8217;est-à-dire que les 2 peers peuvent désormais <strong>s'échanger des messages dans une communication bi-directionnelle et full-duplex</strong>.<br></p>
</div>
<div class="paragraph">
<p>Comme le montre le schéma de la <strong>Figure 3</strong>, le peer server peut envoyer plusieurs messages (dans l&#8217;exemple : 1 message à chaque point du match) sans aucune réponse du peer client qui, lui, peut également envoyer des messages à n&#8217;importe quel moment (dans l&#8217;exemple : le pari sur le vainqueur du match).
Chaque peer peut envoyer un message spécifique afin de clôturer la connexion.<br></p>
</div>
<div class="paragraph">
<p>Dans Java EE7, le code côté peer server est en <strong>Java</strong> alors que le code côté peer client est en <strong>Java ou en Javascript</strong>.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_websocket_javascript_api_client">WebSocket Javascript API (Client)</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Pour communiquer à partir d&#8217;une application Web avec un serveur en utilisant le protocole WebSocket, il est nécessaire d&#8217;utiliser <strong>une API cliente en Javascript</strong>. C&#8217;est le W3C qui définit cette API.<br>
La spécification W3C de cette <a href="http://w3.org/TR/websockets/">API Javascript pour WebSocket</a> est en cours de finalisation. <a href="http://www.w3.org/TR/websockets/#websocket">L&#8217;interface WebSocket</a> propose, entre-autres, les éléments suivants :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>un attribut pour l&#8217;URL de connexion au server Endpoint (url)</p>
</li>
<li>
<p>un attribut sur l'état de la connexion (readyState : CONNECTING, OPEN, CLOSING, CLOSED)</p>
</li>
<li>
<p>des <strong>Event-Handler (gestionnaire d'évènement)</strong> pour s&#8217;adapter aux méthodes du cycle de vie des WebSocket, par exemple :</p>
<div class="ulist">
<ul>
<li>
<p>l&#8217;Event-Handler onopen est appelé lorsqu&#8217;une nouvelle connexion est initiée</p>
</li>
<li>
<p>l&#8217;Event-Handler onerror est appelé lorsqu&#8217;une erreur est reçue pendant la communication</p>
</li>
<li>
<p>l&#8217;Event-Handler onmessage est appelé lorsqu&#8217;un message est reçu</p>
</li>
</ul>
</div>
</li>
<li>
<p>les méthodes (send(DOMString data), send(Blob data)) avec lesquelles il est possible d&#8217;envoyer différents types de flux (texte, binaire) vers le serveur Endpoint</p>
</li>
</ul>
</div>
<div id="eg3-callouts" class="exampleblock">
<div class="title">Example 3. Exemple de code Javascript, issue de <a href="http://websocket.org" class="bare">http://websocket.org</a></div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-javascript" data-lang="javascript">var wsUri = "ws://echo.websocket.org/";

function testWebSocket() {

	websocket = new WebSocket(wsUri);
	websocket.onopen = function(evt) { onOpen(evt) };
	websocket.onclose = function(evt) { onClose(evt) };
	websocket.onmessage = function(evt) { onMessage(evt) };
	websocket.onerror = function(evt) { onError(evt) }; }
}

function onOpen(evt) {
	writeToScreen("CONNECTED");
	doSend("WebSocket rocks");
}
function onClose(evt) {
	writeToScreen("DISCONNECTED");
}
function onMessage(evt) {
	writeToScreen('&lt;span style="color: blue;"&gt;RESPONSE: ' + evt.data+'&lt;/span&gt;');
	websocket.close();
}

function onError(evt) {
	writeToScreen('&lt;span style="color: red;"&gt;ERROR:&lt;/span&gt; ' + evt.data);
}
function doSend(message) {
	writeToScreen("SENT: " + message);
	websocket.send(message);
}</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_jsr_386_java_api_pour_websocket">JSR 386 : Java API pour WebSocket</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Le W3C définit donc comment utiliser WebSocket en Javascript, le <strong>Java Communitee Process (JCP)</strong> fait de même pour le monde Java via la JSR 386 .<br>
La JSR 356 définit ainsi une <a href="http://jcp.org/en/jsr/detail?id=356">API Java pour WebSocket</a> qui propose :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>la création d&#8217;un *WebSocket Endpoint* (serveur ou client), nom donné au composant Java capable de communiquer via le protocole WebSocket</p>
</li>
<li>
<p>la possibilité d&#8217;utiliser l&#8217;approche par <strong>annotation Java</strong> ou par programmation</p>
</li>
<li>
<p>la possibilité <strong>d&#8217;envoyer et de consommer des messages</strong> de contrôles, textuels ou binaires via ce protocole</p>
<div class="ulist">
<ul>
<li>
<p>de gérer le message en tant que message complet ou par une séquence de messages partiels</p>
</li>
<li>
<p>envoyer ou recevoir les messages en tant qu&#8217;objets Java (notion d'<strong>encoders/decoders</strong>)</p>
</li>
<li>
<p>envoyer les messages <strong>en synchrone ou en asynchrone</strong></p>
</li>
</ul>
</div>
</li>
<li>
<p>la configuration et la <strong>gestion des sessions WebSocket</strong> (timeout, cookies&#8230;&#8203;)</p>
</li>
<li>
<p>une intégration dans <strong>Java EE Web Profile</strong></p>
</li>
</ul>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
'L&#8217;implémentation de référence Java pour l&#8217;API WebSocket est <a href="https://tyrus.java.net/">le projet Tyrus</a>'
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="_websocket_endpoint_serveur">WebSocket Endpoint : Serveur</h3>
<div class="paragraph">
<p>La transformation d&#8217;un Plain Old Java Object (POJO) vers un <strong>WebSocket Endpoint</strong> de type serveur (c&#8217;est-à-dire capable de gérer des requêtes de plusieurs clients sur une même URI) est <strong>extrêment simple</strong>, puisqu&#8217;il suffit d&#8217;annoter la classe avec <strong>@ServerEndpoint</strong> et une méthode du POJO avec <strong>@OnMessage</strong> :</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">import javax.websocket.OnMessage;
import javax.websocket.ServerEndpoint;

@ServerEndpoint("/echo") <i class="conum" data-value="1"></i><b>(1)</b>
public class EchoServer {

	@OnMessage <i class="conum" data-value="2"></i><b>(2)</b>
	public String handleMessage(String message){
		return "Thanks for the message: " + message;
	}

}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>L&#8217;annotation @ServerEndpoint transforme le POJO en WebSocket Endpoint, l&#8217;attribut <strong>value</strong> est obligatoire afin de préciser l&#8217;URI d&#8217;accès à cet Endpoint</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>la méthode handleMessage sera évoquée lors de chaque message reçu</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_annotations">Annotations</h3>
<div class="paragraph">
<p>L&#8217;API met à disposition plusieurs types d&#8217;annotations afin d'être entièrement compatible avec le procotole WebSocket :</p>
</div>
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 50%;">
<col style="width: 50%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Annotation</th>
<th class="tableblock halign-left valign-top">Rôle</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@ServerEndpoint</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Déclare un Server Endpoint</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@ClientEndpoint</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Déclare un Client Endpoint</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@OnOpen</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Défini la méthode appelée pour gérer l'évenement d&#8217;ouverture de la connexion</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@OnMessage</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Défini la méthode appelée pour gérer l'évenement de réception d&#8217;un message</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@OnError</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Défini la méthode appelée pour gérer l'évenement lors d&#8217;une erreur</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">@OnClose</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Défini la méthode appelée pour gérer l'évenement de clôture de la connexion</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>Les attributs de l&#8217;annotation @ServerEndpoint sont les suivants :</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">value</dt>
<dd>
<p>URI relative ou URI template (ex: "/echo", "/chat/{subscriver-level}")</p>
</dd>
<dt class="hdlist1">decoders</dt>
<dd>
<p>liste de noms de classes utilisées pour décoder les messages entrants</p>
</dd>
<dt class="hdlist1">encoders</dt>
<dd>
<p>liste de noms de classes utilisées pour encoder les messages sortants</p>
</dd>
<dt class="hdlist1">subprotocols</dt>
<dd>
<p>liste de sous-protocoles autorisés (ex: <a href="http://wamp.ws" class="bare">http://wamp.ws</a>)</p>
</dd>
</dl>
</div>
</div>
<div class="sect2">
<h3 id="_encoders_et_decoders">Encoders et Decoders</h3>
<div class="paragraph">
<p>Comme il a été décrit plus tôt dans cet article, le serveur Endpoint peut recevoir différents types de contenu dans les messages : des données au format texte (JSON, XML&#8230;&#8203;) ou au format binaire.<br>
Afin de gérer efficacement les messages provenant des 'peers client' ou à destination de ceux-ci dans le code métier de l&#8217;application, il est possible de créer des classes Java de type <strong>Decoders et Encoders</strong>.</p>
</div>
<div class="paragraph">
<p>Quelque soit l&#8217;algorithme de transformation, il va alors être possible de transformer  :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>le POJO métier vers un flux au format désiré pour l&#8217;envoi (JSON, XML, Binaire&#8230;&#8203;)</p>
</li>
<li>
<p>les flux entrants dans format spécifique (JSON, XML..) vers le POJO métier</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Ainsi, le code de l&#8217;application est organisé de telle façon que la logique métier n&#8217;est pas impactée par le type et le format de flux échangés entre le 'peer serveur' et les 'peers clients'.
Un exemple concret est présenté dans la suite de l&#8217;article.</p>
</div>
</div>
<div class="sect2">
<h3 id="_websocket_endpoint_client">WebSocket Endpoint : Client</h3>
<div class="paragraph">
<p>L&#8217;API propose donc également le support pour créer des Endpoints côté client en Java.</p>
</div>
<div id="eg4-callouts" class="exampleblock">
<div class="title">Example 4. Exemple de Client Endpoint en Java</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">@ClientEndpoint
public class HelloClient {

	@OnMessage
	public String message(String message){
		// traitement
	}
}

WebSocketContainer c = ContainerProvider.getWebSocketContainer();
c.connectToServer(HelloClient.class, "hello");</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_application_us_open">Application US OPEN</h2>
<div class="sectionbody">
<div class="paragraph">
<p>L&#8217;application exemple est déployée sous forme de WAR issue d&#8217;un projet Apache Maven.
Outre la gestion classique du cycle de vie WebSocket, le workflow d&#8217;envoi de messages est le suivant :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>à chaque point du match, les 'peers clients' recoivent les données du match (score, service&#8230;&#8203;)</p>
</li>
<li>
<p>le 'peer client' peut envoyer un message pour parier sur le gagnant du match</p>
</li>
<li>
<p>à la fin du match, les 'peers clients' reçoivent un message contenant le nom du vainqueur</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Tous les messages sont échangés au format JSON.</strong><br></p>
</div>
<div class="paragraph">
<p>L&#8217;arborescence du projet est la suivante :</p>
</div>
<div id="eg5-callouts" class="exampleblock">
<div class="title">Example 5. Structure du projet Maven</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">+ src/main/java
   |+ com.mgreau.wildfly.websocket
      |+ decoders
         |- MessageDecoder.java   <i class="conum" data-value="1"></i><b>(1)</b>
      |+ encoders       <i class="conum" data-value="2"></i><b>(2)</b>
         |- BetMessageEncoder.java
         |- MatchMessageEncoder.java
      |+ messages       <i class="conum" data-value="3"></i><b>(3)</b>
         |- BetMessage.java
         |- MatchMessage.java
         |- Message.java
      |- MatchEndpoint.java    <i class="conum" data-value="4"></i><b>(4)</b>
      |- StarterService.java   <i class="conum" data-value="5"></i><b>(5)</b>
      |- TennisMatch.java      <i class="conum" data-value="6"></i><b>(6)</b>
+ src/main/resources
+ scr/main/webapp
   |+ css
   |+ images
   |- index.html
   |- websocket.js  <i class="conum" data-value="7"></i><b>(7)</b>
pom.xml</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Decode le message JSON provenant du 'peer client' concernant le pari sur le vainqueur en POJO ('BetMessage')</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Encode à destination des 'peers clients', en JSON (via JSON-P), les messages contenant le détail du match et le résultat du pari sur le vainqueur</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>POJOs représentant les types de messages échangés entre peers</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>WebSocket Server Endpoint de l&#8217;application ('peer server')</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>EJB @Startup permettant d&#8217;initialiser l&#8217;application lors du déploiement</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>POJO pour gérer les informations du match</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>Fichier Javascript pour la communication WebSocket du 'peer client' via l&#8217;API Javascript</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_d_pendances_maven_java_ee_7">Dépendances Maven Java EE 7</h3>
<div id="eg6-callouts" class="exampleblock">
<div class="title">Example 6. pom.xml</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;project&gt;
...
&lt;properties&gt;
	&lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;
	&lt;!-- Java EE 7 --&gt;
	&lt;javaee.api.version&gt;7.0&lt;/javaee.api.version&gt;
&lt;/properties

&lt;dependencies&gt;
	&lt;dependency&gt;
		&lt;groupId&gt;javax&lt;/groupId&gt; <i class="conum" data-value="1"></i><b>(1)</b>
		&lt;artifactId&gt;javaee-api&lt;/artifactId&gt;
		&lt;version&gt;${javaee.api.version}&lt;/version&gt;
		&lt;scope&gt;provided&lt;/scope&gt;
	&lt;/dependency&gt;
&lt;/dependencies&gt;
...
&lt;/project&gt;</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>il est important d&#8217;utiliser les dépendances de la spécification Java EE 7 afin de pouvoir déployer l&#8217;application dans plusieurs serveurs d&#8217;applications Java EE sans changement de code (Wildfly, Glassfish&#8230;&#8203;)</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_cr_er_le_server_endpoint">Créer le Server Endpoint</h3>
<div class="paragraph">
<p>Cet Endpoint permet de recevoir les messages concernant les paris sur le vainqueur du match et également d&#8217;envoyer aux 'peers clients' les informations du déroulement du match.</p>
</div>
<div id="eg7-callouts" class="exampleblock">
<div class="title">Example 7. Server Endpoint : MatchEndpoint.java</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">@ServerEndpoint(
		value = "/matches/{match-id}",  <i class="conum" data-value="1"></i><b>(1)</b>
		        decoders = { MessageDecoder.class }, <i class="conum" data-value="2"></i><b>(2)</b>
		        encoders = { MatchMessageEncoder.class, BetMessageEncoder.class } <i class="conum" data-value="3"></i><b>(3)</b>
		)
public class MatchEndpoint {

	private static final Logger logger = Logger.getLogger("MatchEndpoint");

	/* Queue for all open WebSocket sessions */
	static Queue&lt;Session&gt; queue = new ConcurrentLinkedQueue&lt;&gt;();

	@OnOpen
	public void openConnection(Session session,
				@PathParam("match-id") String matchId) {	<i class="conum" data-value="4"></i><b>(4)</b>
	    /* Register this connection in the queue */
	    queue.add(session);
	    session.getUserProperties().put(matchId, true);
	    logger.log(Level.INFO, "Connection opened for game : " + matchId);
	}

	public static void send(MatchMessage msg, String matchId) {
	  try {
	    /* Send updates to all open WebSocket sessions for this match */
	    for (Session session : queue) {
    	  if (Boolean.TRUE.equals(session.getUserProperties().get(matchId))){
	        if (session.isOpen()){
		      session.getBasicRemote().sendObject(msg);	<i class="conum" data-value="5"></i><b>(5)</b>
		      logger.log(Level.INFO, "Score Sent: {0}", msg);
	        }
    	  }
	    }
	  } catch (IOException | EncodeException e) {
	    logger.log(Level.INFO, e.toString());
	  }
	}

	@OnMessage
	public void message(final Session session, BetMessage msg) {	<i class="conum" data-value="6"></i><b>(6)</b>
	    logger.log(Level.INFO, "Received: Bet Match Winner - {0}", msg.getWinner());
	    session.getUserProperties().put("betMatchWinner", msg);
	}
...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>URI pour accéder à cet Endpoint, comme le context-root de l&#8217;application est '/usopen', un exemple d&#8217;URL est ws://&lt;host&gt;:&lt;port&gt;/usopen/matches/1234</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>la classe 'MessageDecoder' permet de transformer le flux JSON entrant pour le pari sur le vainqueur en POJO 'BetMessage'</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>les 2 encodeurs permettent de transformer les POJO 'MatchMessage' et 'BetMessage' en flux JSON</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>l&#8217;annotation @PathParam permet ici d&#8217;extraire l'élément de la requête WS et de passer la valeur (identifiant du match) en paramètre de la méthode, il est ainsi possible de gérer plusieurs matchs avec des clients différents pour chaque match.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Envoi du message concernant le match aux peers connectés, grâce à l''Encoder' il suffit de passer en paramètre un objet 'MatchMessage'</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>Gestion de la réception des messages de pari sur le vainqueur du match, grâce au 'Decoder' la méthode prend en paramètre un objet 'BetMessage'</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_encoder_et_d_coder_les_messages_chang_s">Encoder et Décoder les messages échangés</h3>
<div class="paragraph">
<p>Pour encoder ou décoder les messages échangés entre peers, il suffit d&#8217;implémenter l&#8217;interface adéquate selon le type de message (Texte, Binaire) et le sens de traitement (encodage, décodage), puis de redéfinir la méthode associée.<br>
Dans l&#8217;exemple ci-dessous, il s&#8217;agit de l&#8217;encodeur pour le POJO MatchMessage vers le format JSON. L&#8217;API utilisée pour réaliser ce traitement est une des nouvelles API de Java EE 7 : <a href="http://jcp.org/en/jsr/detail?id=353">Java API for JSON Processiong (JSON-P)</a></p>
</div>
<div id="eg8-callouts" class="exampleblock">
<div class="title">Example 8. src/main/java/com/mgreau/wildfly/websocket/encoders/MatchMessageEncoder.java</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">public class MatchMessageEncoder implements Encoder.Text&lt;MatchMessage&gt; {

	@Override
	public String encode(MatchMessage m) throws EncodeException {
		StringWriter swriter = new StringWriter();
		try (JsonWriter jsonWrite = Json.createWriter(swriter)) {
			JsonObjectBuilder builder = Json.createObjectBuilder();
			builder.add(
				"match",
				Json.createObjectBuilder()
					.add("serve", m.getMatch().getServe())
					.add("title", m.getMatch().getTitle())
					...
			}

			jsonWrite.writeObject(builder.build());
		}
		return swriter.toString();
	}
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_client_web_html5">Client Web HTML5</h3>
<div class="paragraph">
<p>L&#8217;unique page HTML de cette application charge le fichier <strong>websocket.js</strong> pour mettre en oeuvre l&#8217;API Javascript WebSocket et ainsi intéragir avec le Server Endpoint Java.</p>
</div>
<div id="eg9-callouts" class="exampleblock">
<div class="title">Example 9. API Javascript : websocket.js</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="highlight nowrap"><code class="language-javascript" data-lang="javascript">var wsUrl;
if (window.location.protocol == 'https:') {  <i class="conum" data-value="1"></i><b>(1)</b>
	wsUrl = 'wss://' + window.location.host + ':8443/usopen/matches/1234';
} else {
	wsUrl = 'ws://' + window.location.host + ':8000/usopen/matches/1234';
}

function createWebSocket(host) {
	if (!window.WebSocket) {    <i class="conum" data-value="2"></i><b>(2)</b>
	...
	} else {
		socket = new WebSocket(host);   <i class="conum" data-value="3"></i><b>(3)</b>
		socket.onopen = function() {
			document.getElementById("m1-status").innerHTML = 'CONNECTED...';
		};
		socket.onclose = function() {
			document.getElementById("m1-status").innerHTML = 'FINISHED';
		};
		...
		socket.onmessage = function(msg) {
			try {
				console.log(data);
				var obj = JSON.parse(msg.data);     <i class="conum" data-value="4"></i><b>(4)</b>
				if (obj.hasOwnProperty("match")){   <i class="conum" data-value="5"></i><b>(5)</b>
					//title
					m1title.innerHTML = obj.match.title;
					// comments
					m1comments.value = obj.match.comments;
					// serve
					if (obj.match.serve === "player1") {
						m1p1serve.innerHTML = "S";
						m1p2serve.innerHTML = "";
					} else {
						m1p1serve.innerHTML = "";
						m1p2serve.innerHTML = "S";
					}
					..
				}
				...
			} catch (exception) {
				data = msg.data;
				console.log(data);
			}
		}
	}
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Choix du protocole WS selon le type de protocole HTTP utilisé (sécurisé ou non)</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Test du support par le navigateur de l&#8217;API WebSocket</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Création du WebSocket</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Sur l&#8217;Event-Handler onmessage , traitement du flux JSON reçu via le 'peer serveur'</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Test du type d&#8217;objet reçu (Match ou Pari) afin de réaliser le traitement adéquat avec le DOM</td>
</tr>
</table>
</div>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Pour savoir quels sont les <strong>navigateurs compatibles avec l&#8217;API WebSocket</strong>, <a href="http://caniuse.com/#search=websocket">consultez le site caniuse.com</a>. Aujourd&#8217;hui, les dernières versions des navigateurs sont compatibles exceptées pour Opéra mini et Android Browser, qui représentent, à eux deux, seulement 3% du traffic web.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_sources_de_l_exemple_sur_github">Sources de l&#8217;exemple sur Github</h3>
<div class="paragraph">
<p>Vous pouvez <strong>forker le code sur Github</strong> à l&#8217;URL *https://github.com/mgreau/javaee7-websocket*</p>
</div>
<div class="paragraph">
<p>Cette application exemple est très basique, les idées d&#8217;améliorations possibles sont nombreuses : gérer un tournoi avec plusieurs matchs, parier sur d&#8217;autres critères, voir en live les paris des autres internautes&#8230;&#8203;</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>'Une feature, qui serait particulièrement intéressante techniquement, serait de créer un nouveau type de pari sur <strong>la zone de terrain des points gagnants</strong>. Il suffit de dessiner le terrain grâce à l&#8217;API HTML5 Canvas et de gérer les coordonnées de l&#8217;emplacement cliqué par l&#8217;internaute (comme zone gagnante) puis de les comparer aux coordonnées réelles lors d&#8217;un point gagnant.'</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_construire_et_d_ployer_le_war">Construire et Déployer le WAR</h3>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Pré-requis :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>JDK 7</p>
</li>
<li>
<p>Apache Maven 3.0.4+</p>
</li>
<li>
<p>Serveur d&#8217;applications Java EE 7 : Wildfly 8 ou Glassfish 4</p>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Pour créer l&#8217;archive WAR, il suffit d&#8217;executer la commande Apache Maven ci-dessous ;</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">mvn clean package</code></pre>
</div>
</div>
<div class="paragraph">
<p>Si vous utilisez Wildfly, le déploiement est automatique (le serveur doit être démarré) avec la commande ci-dessous :</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">mvn jboss-as:deploy</code></pre>
</div>
</div>
<div class="paragraph">
<p>Il suffit ensuite d&#8217;accéder à l&#8217;URL : <a href="http://localhost:8080/usopen/" class="bare">http://localhost:8080/usopen/</a></p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_performances_websocket_vs_rest">Performances : WebSocket vs REST</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Afin d&#8217;avoir des métriques concernant les performances de ce nouveau protocole, Arun Gupta a developpé <a href="https://github.com/arun-gupta/javaee7-samples/tree/master/websocket/websocket-vs-rest">une application qui permet de comparer les temps d&#8217;execution</a> d&#8217;un même traitement réalisé avec du code développé en utilisant les technologies WebSocket et REST.</p>
</div>
<div class="paragraph">
<p>Les 2 endpoints de l&#8217;application (REST Endpoint et WebSocket Endpoint) ne font que renvoyer le flux qu&#8217;ils recoivent. L&#8217;interface Web de cette application permet de définir la taille du message et le nombre de fois que ce message doit être envoyé avant la fin du test.</p>
</div>
<div class="paragraph">
<p>Les résultats de ses tests, présentés ci-dessous, sont éloquents :</p>
</div>
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Type de Requête</th>
<th class="tableblock halign-left valign-top">Temps execution<br>
<strong>REST Endpoint</strong></th>
<th class="tableblock halign-left valign-top">Temps execution<br>
<strong>WebSocket Endpoint</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Envoi de 10 messages de 1 byte</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">220 ms</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">7 ms</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Envoi de 100 messages de 10 bytes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">986 ms</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">57 ms</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Envoi de 1000 messages de 100 bytes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">10 210 ms</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">179 ms</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Envoi de 5000 messages de 1000 bytes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">54 449 ms</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1202 ms</p></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="sect1">
<h2 id="_r_f_rences_pour_tout_savoir_sur_les_websocket">Références pour tout savoir sur les WebSocket</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Je vous recommande plus particulièrement les conférences d' <a href="https://twitter.com/arungupta">Arun Gupta</a>, qui vous permettent, en moins d'1 heure, de tout connaître/comprendre sur la technologie WebSocket en général et sur l&#8217;API Java en particulier.<br>
Pour des informations plus avancées, l&#8217;idéal reste les spécifications IETF, W3C et Java.</p>
</div>
<div class="ulist bibliography">
<ul class="bibliography">
<li>
<p><a href="http://tools.ietf.org/html/rfc6455">RFC 6455: The WebSocket Protocol</a> - 'Spécification IETF'</p>
</li>
<li>
<p><a href="http://w3.org/TR/websockets/">W3C: The WebSocket API</a> - 'Spécification W3C' (Candidate Recommandation)</p>
</li>
<li>
<p><a href="http://jcp.org/en/jsr/detail?id=356">JSR 356: Java API for WebSocket Protocol</a> - 'Spécification Java'</p>
</li>
<li>
<p><a href="https://glassfish.java.net/adoptajsr/jsr356.html">Adopt a JSR - JSR 356</a></p>
</li>
<li>
<p><a href="http://www.youtube.com/watch?v=QqbuDFIT5To">Java EE 7 &amp; WebSocket API</a> - 'Conférence Arun Gupta SF' (à partir de la 46e minute)</p>
</li>
<li>
<p><a href="http://www.parleys.com/play/51c1cceae4b0ed8770356828/chapter4/about">Getting Started with WebSocket and SSE</a> - 'Conférence Arun Gupta Devoxx UK 2013'</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>'Cet article a été structuré en se basant sur la conférence Devoxx UK 2013.'</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Cet article a introduit, grâce à un exemple concret, <strong>le protocole WebSocket, l&#8217;API WebSocket HTML5 et l&#8217;API Java pour les WebSocket sortie avec Java EE 7</strong>. Il était déjà possible d&#8217;utiliser les WebSocket en Java grâce à des frameworks comme Atmosphere mais il manquait un standard.<br>
Aujourd&#8217;hui tous <strong>les standards sont finalisés ou en passe de l'être</strong>, cette nouvelle technologie répond à un besoin précis et est prometteuse en terme de performance. Pour qu&#8217;elle soit massivement utilisée, il faudra tout de même que ce protocole soit autorisée dans les entreprises là où bien souvent seul le protocole HTTP est disponible.</p>
</div>
</div>
</div>]]></description><link>https://mgreau.com/posts/2013/09/27/javaee7-api-websocket-html5.html</link><guid isPermaLink="true">https://mgreau.com/posts/2013/09/27/javaee7-api-websocket-html5.html</guid><category><![CDATA[[FR]]]></category><category><![CDATA[ Java EE 7]]></category><category><![CDATA[ Java EE]]></category><category><![CDATA[ WebSocket]]></category><category><![CDATA[ HTML5]]></category><category><![CDATA[ WildFly]]></category><dc:creator><![CDATA[Maxime Gréau]]></dc:creator><pubDate>Fri, 27 Sep 2013 00:00:00 GMT</pubDate></item><item><title><![CDATA[XStream on EAP 6 with Sun14ReflectionProvider]]></title><description><![CDATA[<div class="paragraph">
<p>If you want to use the Sun14ReflectionProvider with XStream, you have to add a dependency to the "sun.jdk" module.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;module xmlns="urn:jboss:module:1.0" name="mymodule"&gt;
  ...
  &lt;dependencies&gt;
    &lt;module name="sun.jdk" /&gt;
  &lt;/dependencies&gt;
&lt;/module&gt;</code></pre>
</div>
</div>]]></description><link>https://mgreau.com/posts/2013/08/26/xstream-and-JBossEAP6.html</link><guid isPermaLink="true">https://mgreau.com/posts/2013/08/26/xstream-and-JBossEAP6.html</guid><category><![CDATA[Java EE]]></category><category><![CDATA[ JBoss]]></category><category><![CDATA[ EAP]]></category><category><![CDATA[ XStream]]></category><dc:creator><![CDATA[Maxime Gréau]]></dc:creator><pubDate>Mon, 26 Aug 2013 00:00:00 GMT</pubDate></item><item><title><![CDATA[Yahoo! Mail, t'es une messagerie, t'as pas de conversation ? non mais allô quoi !]]></title><description><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Cet article expose mon point de vue sur l&#8217;ergonomie, l&#8217;expérience utilisateur et les services rendus (ou pas) à l&#8217;internaute par Yahoo! Mail et Gmail. Je me base sur l&#8217;expérience de mon utilisation de Yahoo! Mail depuis 14 ans et de Gmail depuis 7 ans.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>TIME: 15mn</p>
</div>
<div class="paragraph">
<p>Alors que <a href="http://www.google.com/mail/help/intl/fr/about.html">Gmail</a> vient de fêter, au début du mois, <a href="http://gmailblog.blogspot.fr/2013/04/gmail-9-years-and-counting.html">ses 9 ans d&#8217;existence</a>, Yahoo! a sorti fin 2012 la dernière version de Yahoo! Mail créé en 1997. C&#8217;est l''occasion de faire un bilan de ces 2 acteurs principaux sur ce marché du webmail gratuit (Il ne sera pas question du 3e acteur, Hotmail, car je ne l&#8217;ai jamais testé). Il faut savoir que ces 3 acteurs revendiquent <strong>chacun plus de 280 millions d&#8217;utilisateurs</strong> dans le monde !</p>
</div>
<div class="imageblock">
<div class="content">
<img src="https://mgreau.com/posts/images/Gmail_yahoo_logo.png" alt="Logos Yahoo!Mail Gmail">
</div>
</div>
<div class="paragraph">
<p>Il existe déjà un certain nombre de comparatifs de ces 2 webmails mais ils se focalisent trop souvent sur des éléments tels que le nombre de Gigas d&#8217;espace ou les protocoles activés&#8230;&#8203; et pas sur les éléments importants d&#8217;une boîte de messagerie dans un navigateur à savoir : <strong>les fonctionnalités de base disponibles et l&#8217;expérience utilisateur associée</strong>.
Autrement dit, comment faire pour nous rendre la vie la plus simple possible sur les principales actions que nous réalisons tous (geek ou novice) chaque jour : écrire un mail, lire un mail, répondre, faire suivre, envoyer une pièce jointe, rechercher dans nos messages&#8230;&#8203;</p>
</div>
</div>
<div id="toc" class="toc">
<div id="toctitle">Table des matières de l'article</div>
<ul class="sectlevel1">
<li><a href="#_yahoo_mail_une_messagerie_rest_e_dans_les_ann_es_90">Yahoo! Mail, une messagerie restée dans les années 90</a>
<ul class="sectlevel2">
<li><a href="#_des_mauvais_choix_techniques_et_ergonomiques">Des mauvais choix techniques et ergonomiques</a></li>
</ul>
</li>
<li><a href="#_le_mode_conversation_une_fonctionnalit_indispensable">Le mode "Conversation" : une fonctionnalité indispensable !</a></li>
<li><a href="#_des_options_qui_font_la_diff_rence">Des options qui font la différence</a>
<ul class="sectlevel2">
<li><a href="#_oups_avec_la_pi_ce_jointe_c_est_mieux">Oups, avec la pièce jointe c&#8217;est mieux :-)</a></li>
<li><a href="#_trop_tard_j_ai_cliqu">Trop tard, j&#8217;ai cliqué !</a></li>
<li><a href="#_il_est_rang_o_d_j_ce_mail_si_si_je_l_ai_quelque_part">Il est rangé où déjà ce mail ? si si je l&#8217;ai quelque part !</a></li>
<li><a href="#_tout_message_est_d_abord_un_brouillon">Tout message est d&#8217;abord un brouillon !</a></li>
<li><a href="#_mais_encore">Mais encore</a></li>
</ul>
</li>
<li><a href="#_du_renouveau_avec_la_nouvelle_ceo_marissa_mayer_venue_de_google">Du renouveau avec la nouvelle CEO Marissa Mayer ?&#8230;&#8203;venue de Google :)</a></li>
<li><a href="#_merci_gmail_une_r_volution_en_2013_pour_yahoo_mail">Merci Gmail. Une révolution en 2013 pour Yahoo! Mail ?</a></li>
</ul>
</div>
</div>
<div class="sect1">
<h2 id="_yahoo_mail_une_messagerie_rest_e_dans_les_ann_es_90">Yahoo! Mail, une messagerie restée dans les années 90</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Depuis 1999 et la création de mon adresse mail <a href="http://yahoo.fr/">@yahoo.fr</a>, j&#8217;ai beau cherché, je ne vois pas de réelle évolution réalisée par Yahoo! pour sa messagerie si populaire. Je parle évidemment en terme de fonctionnalités pour l&#8217;utilisateur (même si en terme technique au niveau de l&#8217;interface Web on peut presque en dire autant). C&#8217;est vrai, à part les modifications "standards" liées aux évolutions des technologies Web, je ne vois pas ce que Yahoo! Mail a apporté de nouveau à ce jour.</p>
</div>
<div class="sect2">
<h3 id="_des_mauvais_choix_techniques_et_ergonomiques">Des mauvais choix techniques et ergonomiques</h3>
<div class="paragraph">
<p>Yahoo! Mail n&#8217;a cessé de prendre les mauvaises décisions pour son produit phare ! Heureusement que son public est en partie composé (je présume) de personnes qui n&#8217;osent pas passer le cap de changer de client de messagerie par peur du changement justement; sinon Yahoo! Mail aurait fermé depuis longtemps.</p>
</div>
<div class="paragraph">
<p>Sa première version "classique" est celle que j&#8217;ai gardé le plus longtemps même (et surtout) après la première version "Web 2.0".  Cette première version "Web 2.0" entièrement réalisée à base de composants Javascript se voulait une simulation, dans un navigateur Web, d&#8217;un client de messagerie installé localement sur le système d&#8217;exploitation (type Outlook ou Thunderbird) . Double erreur ! Tout d&#8217;abord techniquement cette version n'était vraiment pas au point ! Un temps d&#8217;initialisation interminable dans les premières versions du au chargement de la majorité des messages (et de leur contenu ?) avant le premier clic de l&#8217;utilisateur.  Je ne vous parle même pas du "déroulement de souris" dans la boîte de réception qui vous affichait un sablier pendant un temps infini. Ensuite côté ergonomie, elle simulait une version "client local" sans l’exhaustivité des fonctionnalités et ajouté aux temps de réponses intolérables, le résultat : une frustration générale.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_le_mode_conversation_une_fonctionnalit_indispensable">Le mode "Conversation" : une fonctionnalité indispensable !</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Vous avez sûrement des amis ou de la famille avec qui vous échangez des mails pour une fête, un anniversaire&#8230;&#8203;et vous vous retrouvez avec 20 mails dans votre messagerie car tout le monde répond à l&#8217;ensemble des destinataires. Sur Yahoo! Mail ça donne <strong>20 lignes dans votre Boite de Réception avec comme sujet "Re:Re:Re:Re:Re:Re:Re:Re:Re:Re:Re:Re:Re:Re:Re:Cadeau Anniversaire Jean-Michel"</strong>
Quel est l&#8217;intérêt d&#8217;avoir autant d&#8217;espace pris sur votre écran pour une discussion avec des réponses du type "OK pour moi" ? Si, en plus, vous avez plusieurs discussions différentes de ce type dans votre Boîte de réception, il devient impossible de retrouver le fil rapidement.</p>
</div>
<div id="yahoo-rere" class="imageblock">
<div class="content">
<img src="https://mgreau.com/posts/images/yahoo_rerere.png" alt="Yahoo! Re:re:re">
</div>
<div class="title">Figure 1. Exemple de discussion avec 1 ligne pour chaque message avec des "Re: Re: Re: Re: Re: Re: Re:"</div>
</div>
<div class="paragraph">
<p>Depuis longtemps dans Gmail  vous avez par défaut le <strong>mode conversation activé</strong> qui vous permet d&#8217;avoir une <strong>seule ligne pour toute la conversation</strong> puis d&#8217;afficher le détail et le déroulement de la conversation à plusieurs lors du clic sur cette discussion. Ainsi, vous suivez beaucoup plus facilement le fil de discussion et l'écran principal de votre boîte de réception est épuré de tous ces messages.</p>
</div>
<div id="gmail_discussion" class="imageblock">
<div class="content">
<img src="https://mgreau.com/posts/images/gmail_discussion.png" alt="Gamil discussion">
</div>
<div class="title">Figure 2. Même type de discussion avec Gmail et le mode conversation : 1 ligne dans la boîte de réception puis le détail lors du clic</div>
</div>
<div class="paragraph">
<p>C&#8217;est pour moi une <strong>fonctionnalité majeure, indispensable vu le nombre de mails envoyés aujourd&#8217;hui quotidiennement</strong> <a href="http://www.arobase.org/culture/chiffres-email.htm">39 milliards en 2007 de personne à personne)</a> comparé à 10 ans en arrière. Cette option peut tout de même être désactivée pour les personnes réfractèrent à ce mode de lecture des discussions.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_des_options_qui_font_la_diff_rence">Des options qui font la différence</h2>
<div class="sectionbody">
<div class="paragraph">
<p>90% des fonctionnalités d&#8217;un webmail sont communes à tous ceux qui proposent un logiciel de ce type. Il faut donc <strong>faire la différence sur les 10% restants</strong>. Et là que propose Yahoo! Mail de plus que les autres, que le standard ?&#8230;&#8203;pas grand chose&#8230;&#8203;j&#8217;ai même envie de dire RIEN ! Des exemples ? En voici quelques-uns,  ce n&#8217;est bien sûr pas vital mais ce sont des petits plus très pratiques que l&#8217;on se doit d&#8217;attendre d&#8217;une multi-nationale comme Yahoo!, <a href="http://news.cnet.com/8301-1023_3-57543177-93/gmail-edges-hotmail-as-worlds-top-e-mail-service/">encore leader de la messagerie Web aux Etats-Unis fin 2012</a>.</p>
</div>
<div class="sect2">
<h3 id="_oups_avec_la_pi_ce_jointe_c_est_mieux">Oups, avec la pièce jointe c&#8217;est mieux :-)</h3>
<div class="paragraph">
<p>Vous devez avoir souri en lisant ce titre, non ? Combien de fois cela vous est-il arrivé ? J&#8217;ai plutôt envie de vous demander : "<strong>Cela fait combien d&#8217;années que cela vous est arrivé pour la première fois ? 5 ans ? 10 ans ?</strong>". Et depuis, toujours rien de nouveau. Pourtant, techniquement ce n&#8217;est vraiment pas compliqué d&#8217;essayer de limiter ce genre d&#8217;erreur et <strong>donc d&#8217;aider l&#8217;utilisateur</strong>.</p>
</div>
<div class="paragraph">
<p>Clairement, soit vous envoyez une pièce jointe directement sans écrire de texte dans le contenu du message et là le risque d&#8217;erreur est quasi nul, soit vous saisissez un texte avant et c&#8217;est à ce moment là que l&#8217;oubli peut se produire.
Dans ce deuxième cas, il y a de (très) fortes chances que vous fassiez référence au fait que vous allez envoyer une pièce jointe, non ? Il suffit donc d&#8217;analyser le contenu du message saisi avant l&#8217;envoi du mail à la recherche de mots clés tels que "pièce jointe", "pj", "fichier joint"&#8230;&#8203; et de prévenir l&#8217;utilisateur qu&#8217;il a peut-être oublié quelque-chose.
Gmail propose d&#8217;activer cette fonctionnalité et vous préviens par une boîte de dialogue lors de la tentative d&#8217;envoi du message :</p>
</div>
<div id="gmail_oups_pj" class="imageblock">
<div class="content">
<img src="https://mgreau.com/posts/images/gmail_oups_pj.png" alt="Gmail Oups PJ">
</div>
<div class="title">Figure 3. Boîte de dialogue pour vous prévenir que vous avez peut-être oublié la pièce jointe</div>
</div>
<div class="paragraph">
<p>Pour ceux qui mettraient en avant le fait que Gmail analyse ici le contenu de vos messages, sachez que lorsque vous faîtes vos courses en magasin, vôtre parcours est analysé à votre insu par les caméras afin d&#8217;optimiser par la suite la vente des produits. Ne pensez donc pas que le fait que Yahoo! Mail ne propose pas cette fonctionnalité signifie qu&#8217;il n&#8217;analyse pas le contenu de vos messages également.</p>
</div>
</div>
<div class="sect2">
<h3 id="_trop_tard_j_ai_cliqu">Trop tard, j&#8217;ai cliqué !</h3>
<div class="paragraph">
<p>Combien de fois vous vous êtes dit après avoir cliqué sur le bouton ENVOYER : "Mince! il y avait qui en copie ??" ou "Arg, j&#8217;ai oublié machin et truc" ou encore "Zut, je voulais écrire ça aussi". Trop tard ??? Non, pas avec Gmail !
Et oui, quand on y pense, qu&#8217;est-ce que ça peut bien vous faire que votre message soit envoyé sur les serveurs destinataires *immédiatement après votre clic ou 5 secondes plus tard *(à part dans les services informatiques où les collègues entrent dans le bureau en disant "je viens de t&#8217;envoyer un mail il y a 2 secondes, tu l&#8217;as reçu ?!?" ). Vous ne maîtrisez de toute façon pas le chemin et le temps que va prendre votre message dans "le Cloud" pour se rendre à destination, et vous maîtrisez encore moins le moment où votre destinataire va être disponible pour lire votre message.</p>
</div>
<div class="paragraph">
<p>Gmail propose donc une option à activer qui permet de <strong>réellement envoyer le message après un délai paramètrable en secondes</strong>. Ainsi, si vous décidez que votre mail sera réellement envoyé 5 secondes après votre action d&#8217;envoi, vous aurez la possibilité de cliquer sur un lien "Annuler" pendant ce laps de temps. Sans action de votre part, le mail est envoyé automatiquement après 5 secondes, c&#8217;est donc totalement transparent pour vous sauf quand vous en avez besoin, génial non ? Encore une fois, il n&#8217;y a rien d&#8217;extraordinaire techniquement derrière ça, <strong>c&#8217;est simple, activable ou non par l&#8217;utilisateur et ça peut empêcher des désagréments importants</strong> (l&#8217;oubli que le chef était en copie&#8230;&#8203;.)</p>
</div>
<div id="gmail_annuler_envoi" class="imageblock">
<div class="content">
<img src="https://mgreau.com/posts/images/gmail_annuler_envoi.png" alt="Gmail Annuler Envoi">
</div>
<div class="title">Figure 4. Vous avez un délai pour cliquer sur "Annuler" afin de ne pas envoyer le mail, passé ce délai le message est automatiquement réellement envoyé</div>
</div>
</div>
<div class="sect2">
<h3 id="_il_est_rang_o_d_j_ce_mail_si_si_je_l_ai_quelque_part">Il est rangé où déjà ce mail ? si si je l&#8217;ai quelque part !</h3>
<div class="paragraph">
<p>Vous avez sûrement déjà utilisé Microsoft Windows, là où on organise ses fichiers dans une arborescence de dossiers et donc si un document répond à plusieurs thèmes, il faut choisir un des dossiers ou dupliquer le document en question ou encore créer un raccourci. Et bien Yahoo! Mail fonctionne comme ça encore aujourd&#8217;hui (sans l&#8217;option raccourci) !! On déplace donc un mail dans un dossier, du coup il n&#8217;est plus dans la boîte de réception. La boîte de réception est donc un dossier comme les autres et <strong>un message ne peut apparaître que dans un seul dossier.</strong></p>
</div>
<div class="paragraph">
<p>Avec Gmail  tout est géré avec la <strong>notion de "libellé" et non pas de dossier</strong>. Ainsi chaque message est associé à un ou plusieurs libellés, il est donc possible de d&#8217;associer le message a des libellés tout en le gardant dans la boîte de réception (qui est un libellé particulier) ou de l&#8217;archiver (autre libellé) pour ne pas qu&#8217;il perturbe la boîte de réception. Les libellés peuvent être imbriqués et ils sont personnalisables graphiquement (couleur, police) pour mieux repérer rapidement les messages associés à ces libellés. On peut ensuite réaliser des recherches uniquement sur certains libellés. Bref, on peut faire au minimum la même chose qu&#8217;avec les dossier Yahoo! Mail mais surtout on dispose de 10 fois plus de possibilités pour organiser nos messages et donc les retrouver facilement.</p>
</div>
<div id="gmail_nested-labels" class="imageblock">
<div class="content">
<img src="https://mgreau.com/posts/images/nested-labels.png" alt="Gmail Labels">
</div>
<div class="title">Figure 5. Gestion des labels Gmail</div>
</div>
</div>
<div class="sect2">
<h3 id="_tout_message_est_d_abord_un_brouillon">Tout message est d&#8217;abord un brouillon !</h3>
<div class="paragraph">
<p>Avec Gmail, lorsque vous commencez à écrire un mail, c&#8217;est-à-dire que vous avez débuté votre saisie de texte, automatiquement (sans action de votre part) votre message est un brouillon enregistré qui disparaîtra dès l&#8217;envoi du mail ou qui vous fera une sauvegarde si votre PC s'éteint ou votre navigateur plante violemment (si si ça arrive souvent dans ces moments là). Sur Yahoo! Mail, il faut faire la première démarche de dire "Oui je veux l&#8217;enregistrer comme brouillon" pour qu&#8217;ensuite il se comporte comme Gmail ou attendre 5 minutes pour qu&#8217;il le fasse automatiquement.</p>
</div>
<div class="paragraph">
<p>Il faut s&#8217;interroger, combien de fois vous vous dîtes : "Tiens je vais écrire un brouillon", Non la majorité du temps, vous écrivez un message qui, s&#8217;il n&#8217;est pas envoyé dans la foulée pour des raisons X ou Y, doit être enregistré sans une action de votre part, c&#8217;est implicite. C&#8217;est comme si vous commenciez un message sur un bout de papier pour votre femme, que vous entendez votre enfant pleurer dehors car il est tombé de la balançoire, vous revenez 20 minutes plus tard pour finir votre message&#8230;&#8203;et là plus rien !! Parce qu&#8217;avant de partir en courant, vous n&#8217;avez pas dit "Attends, enregistre mon travail avant que je revienne".</p>
</div>
</div>
<div class="sect2">
<h3 id="_mais_encore">Mais encore</h3>
<div class="paragraph">
<p>Ce sont <strong>quelques exemples parmi tant d&#8217;autres</strong> : Gmail propose aussi une sécurité renforcée avec la validation en 2 étapes lors de la connexion à votre compte (un code temporaire, reçu par sms, est demandé après le mot de passe) ou encore la proposition automatique de destinataires souvent utilisés lors de l’écriture d&#8217;un mail à destination de plus de 2 personnes&#8230;&#8203; Google met donc à disposition de l&#8217;utilisateur une liste très intéressante de fonctionnalités qu&#8217;il est toujours possible de désactiver si elles ne nous satisfont pas.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_du_renouveau_avec_la_nouvelle_ceo_marissa_mayer_venue_de_google">Du renouveau avec la nouvelle CEO Marissa Mayer ?&#8230;&#8203;venue de Google :)</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Il y a longtemps que je souhaite écrire cet article et, depuis, une <a href="http://yodel.yahoo.com/blogs/product-news/introducing-yahoo-mail-12773.html">nouvelle version de Yahoo! Mail est sortie fin 2012</a>. Cette annonce correspond à la première version depuis l&#8217;arrivée à la tête de Yahoo! d&#8217;une ex-employée de Google, <a href="http://fr.wikipedia.org/wiki/Marissa_Mayer">Marissa Mayer</a>. Dans cette nouvelle version, on perçoit immédiatement une volonté d&#8217;aller dans le sens du renouveau : <strong>simplifier et épurer l&#8217;interface pour la rendre plus intuitive et optimiser la rapidité de fonctionnement</strong>. Après quelques jours d&#8217;utilisation (pour cet article justement), je peux confirmer que c&#8217;est la meilleure version de Yahoo! Mail que j&#8217;ai utilisé depuis&#8230;&#8203; Yahoo! Mail classique.</p>
</div>
<div class="paragraph">
<p>Mais, on peut aussi noter, à juste titre, que dans cette annonce <strong>il n&#8217;y a aucune nouvelle fonctionnalité d&#8217;annoncée !</strong> Peut-être pour la prochaine version ? Il faudra tout d&#8217;abord rattraper le retard sur les fonctionnalités majeures indispensables en 2013. Cela a commencé avec <a href="http://yodel.yahoo.com/blogs/product-news/yahoo-mail-dropbox-team-attachments-easier-13210.html">l&#8217;association récente de Yahoo! Mail avec Dropbox</a> là où <a href="http://gmailblog.blogspot.fr/2012/11/gmail-and-drive-new-way-to-send-files.html">Gmail propose l&#8217;utilisation de Google Drive depuis fin 2012</a> pour la gestion des pièces jointes volumineuses.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_merci_gmail_une_r_volution_en_2013_pour_yahoo_mail">Merci Gmail. Une révolution en 2013 pour Yahoo! Mail ?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>J&#8217;ai (volontairement) été un peu dur avec Yahoo! Mail dans cet article mais cela vient de ma frustration de voir comment cette messagerie utilisée par autant de personnes (280 millions fin 2012) et gérée par une société aussi importante que Yahoo! peut avoir si peu évoluée au fil des années. Cette impression qu&#8217;aucun bon choix n&#8217;ait été fait après la version "classique". Je suis peut-être aussi influencé par mon métier qui fait que je me soucie de choses auxquelles la majorité des utilisateurs ne prêtent guère attention. Les éventuels commentaires qui suivront ce post m'éclairciront sûrement sur ces points. Seulement quand je voie les efforts réalisés par Google avec Gmail pour inventer de nouvelles fonctionnalités, je me dis que nous les utilisateurs pourrions avoir encore davantage de possibilités si la concurrence était plus accrue.</p>
</div>
<div class="paragraph">
<p>J&#8217;ai l&#8217;impression que la stratégie de Marissa Mayer va dans ce sens : améliorer l&#8217;expérience utilisateur des internautes qui utilisent massivement ce service.</p>
</div>
<div class="paragraph">
<p>Vous l&#8217;avez certainement compris, <strong>après avoir commencé avec Yahoo! Mail, je suis devenu pro-Gmail</strong> depuis quelques années maintenant mais je ne désespère pas de voir Yahoo! Mail s&#8217;améliorer. D&#8217;ailleurs, je leur propose un exemple d&#8217;ajout de fonctionnalité dans la version gratuite de leur webmail : l&#8217;accusé de lecture.</p>
</div>
<div class="paragraph">
<p>L&#8217;accusé de réception c&#8217;est bien, mais il est demandé lors du clic sur le sujet d&#8217;un message donc il permet de confirmer à l&#8217;expéditeur que le message est bien arrivé à destination (en gros de valider le fonctionnement des serveurs de messagerie !) mais le plus intéressant pour un utilisateur est d'être averti que le destinataire a bien pris connaissance du contenu du message envoyé; donc il est plus judicieux de proposer une action du style "Confirmer la lecture ?" à l&#8217;utilisateur lorsqu&#8217;il a potentiellement lu le message en entier (en bas de l’écran) ou lorsqu&#8217;il quitte le message (retour à la boite de réception&#8230;&#8203;).</p>
</div>
<div class="paragraph">
<p>Au passage donc, <strong>Merci aux équipes de Google</strong> pour leur travail assez exceptionnel tout de même, et allez <strong>Yahoo! Mail, faites nous quelque chose de nouveau</strong> !! Ajoutez une fonctionnalité qui n&#8217;existe pas encore chez vos concurrents. Il est évident qu&#8217;il reste encore plein de choses à proposer aux internautes pour les aider dans l&#8217;utilisation quotidienne de leurs mails, des petites choses souvent simples mais efficaces.</p>
</div>
<div class="paragraph">
<p>Liens référencés dans cette article :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Gmail Blog officiel : <a href="http://www.google.com/mail/help/intl/fr/about.html" class="bare">http://www.google.com/mail/help/intl/fr/about.html</a></p>
</li>
<li>
<p>Yahoo! Mail Blog officiel :</p>
</li>
<li>
<p>Marissa Mayer :</p>
</li>
<li>
<p>Gmail, 9 ans :</p>
</li>
<li>
<p>Yahoo! Mail :</p>
</li>
<li>
<p>Yahoo! Mail et Dropbox :</p>
</li>
<li>
<p>Gmail et Google Drive :</p>
</li>
<li>
<p>Le nouveau Yahoo! Mail :</p>
</li>
<li>
<p>Yahoo! Mail n°1 aux USA :</p>
</li>
<li>
<p>Email en chiffres :</p>
</li>
<li>
<p>Les labels Gmail :</p>
</li>
</ul>
</div>
</div>
</div>]]></description><link>https://mgreau.com/posts/2013/04/24/yahoomail-gmail-user-experience.html</link><guid isPermaLink="true">https://mgreau.com/posts/2013/04/24/yahoomail-gmail-user-experience.html</guid><category><![CDATA[email]]></category><category><![CDATA[ Yahoo]]></category><category><![CDATA[ Google]]></category><category><![CDATA[ GMail]]></category><dc:creator><![CDATA[Maxime Gréau]]></dc:creator><pubDate>Wed, 24 Apr 2013 00:00:00 GMT</pubDate></item><item><title><![CDATA[[Talk] Google Cloud Endpoints @ DevFest 2012]]></title><description><![CDATA[<div class="paragraph">
<p>My Session about <strong>Google Cloud Endpoints</strong> at <a href="http://devfest2012.gdgnantes.com/sessions"><strong>DevFest Nantes 2012</strong></a> with a Web application (Bacbkone JS, HTML5, AppEngine) and Android demo:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><span class="icon"><i class="fa fa-file"></i></span> <a href="http://fr.slideshare.net/mgreau/google-cloud-endpoints-dev-fest-nantes2012">Slides Google Cloud Endpoints DevFest @ <strong>Slideshare</strong></a></p>
</li>
<li>
<p><span class="icon"><i class="fa fa-youtube"></i></span> <a href="https://www.youtube.com/watch?v=qbphOvgKcgg&amp;feature=plcp">Vidéo Google Cloud Endpoints DevFest @ <strong>Youtube</strong></a></p>
</li>
<li>
<p><span class="icon"><i class="fa fa-code"></i></span> <a href="https://github.com/mgreau/appengine-endpoints-booking">Source Code @ <strong>Github</strong></a></p>
</li>
</ul>
</div>
<iframe src="//fr.slideshare.net/slideshow/embed_code/key/1XcrsJWD1PvLhf" width="100%" height="355" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"> <strong> <a href="//fr.slideshare.net/mgreau/google-cloud-endpoints-dev-fest-nantes2012" title="Google Cloud Endpoints - DevFest - Nantes 2012" target="_blank">Google Cloud Endpoints - DevFest - Nantes 2012</a> </strong> from <strong><a href="//www.slideshare.net/mgreau" target="_blank">Maxime Gréau</a></strong> </div>
<div class="videoblock">
<div class="content">
<iframe src="https://www.youtube.com/embed/qbphOvgKcgg?rel=0" frameborder="0" allowfullscreen></iframe>
</div>
</div>]]></description><link>https://mgreau.com/posts/2012/11/09/google-cloud-endpoints.html</link><guid isPermaLink="true">https://mgreau.com/posts/2012/11/09/google-cloud-endpoints.html</guid><category><![CDATA[Google]]></category><category><![CDATA[ Android]]></category><category><![CDATA[ AppEngine]]></category><category><![CDATA[ Javascript]]></category><category><![CDATA[ DevFest]]></category><category><![CDATA[ DevFest2012]]></category><category><![CDATA[ talk]]></category><dc:creator><![CDATA[Maxime Gréau]]></dc:creator><pubDate>Fri, 09 Nov 2012 00:00:00 GMT</pubDate></item><item><title><![CDATA[Subversion - corriger les propriétés d'une révision SVN]]></title><description><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Language : FR / Temps de lecture : 10mn
</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Ce post explique comment <strong>mettre à jour les propriétés SVN d&#8217;une révision donnée</strong> d&#8217;un repository. Et notamment la propriété svn:date qui, lorsqu&#8217;elle est absente sur certaines révisions, bloque les checkout via le client <a href="http://svnkit.com/">SVNKit</a> (utilisé par défaut dans le <a href="https://wiki.jenkins-ci.org/display/JENKINS/Subversion+Plugin">plugin Subversion de Jenkins CI)</a>.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Suite à des traitements d&#8217;administration sur des référentiels Apache Subversion 1.6, les jobs Jenkins CI ne parviennent plus à récupérer les sources d&#8217;un référentiel.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_les_sympt_mes_du_probl_me">Les symptômes du problème</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>les jobs Jenkins ne parviennent plus à faire des "checkout" sur un repository</p>
</li>
<li>
<p>toutes les commandes SVN fonctionnent sur ce même repository via le plugin Eclipse (Subclipse - JavaHL)</p>
</li>
<li>
<p>ces mêmes commandes fonctionnent également en direct sur le serveur SVN</p>
</li>
<li>
<p>le message d&#8217;erreur dans la console Jenkins est le suivant :</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">Building in workspace /.../continuous-integration/jobs/mon-projet/workspace
Checking out a fresh workspace because there's no workspace at
/.../continuous-integration/jobs/mon-projet/workspace

Cleaning local Directory .
Checking out http://serveur/svn/repo1/mon-projet/trunk
ERROR: Failed to check out http://serveur/svn/repo1/mon-projet/trunk

org.tmatesoft.svn.core.SVNException: svn: E175002: REPORT of '/svn/repo1/!svn/vcc/default': 500 Internal Server Error (http://serveur)
 at org.tmatesoft.svn.core.internal.wc.SVNErrorManager.error(SVNErrorManager.java:64)
 ...
 Caused by: svn: E175002: REPORT of '/svn/repo1/!svn/vcc/default': 500 Internal Server Error (http://serveur)
 at org.tmatesoft.svn.core.SVNErrorMessage.create(SVNErrorMessage.java:208)
 at org.tmatesoft.svn.core.SVNErrorMessage.create(SVNErrorMessage.java:189)
 ...
 at org.tmatesoft.svn.core.internal.io.dav.DAVRepository.getDatedRevision(DAVRepository.java:200)
 ... 30 more
FATAL: null
java.lang.NullPointerException</code></pre>
</div>
</div>
<div class="paragraph">
<p>L&#8217;accès à SVN est réalisé avec le <a href="http://httpd.apache.org/docs/2.2/mod/mod_dav.html">module DAV</a>, après une recherche dans les logs d&#8217;Apache, le message réel du serveur SVN est le suivant :</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">Fri Jul 27 10:04:45 2012] [error] [client ] Could not access revision times. &amp;nbsp;[500, #0]</code></pre>
</div>
</div>
<div class="paragraph">
<p>Le problème se confirme lors de l&#8217;exécution des 2 commandes suivantes qui devraient affichées les logs des commit :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Accès aux sources via le système de fichiers</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">svn log --xml -v -r {2008-12-01T00:00:00Z}:{2012-&lt;wbr&gt;&lt;/wbr&gt;07-13T15:00:00Z} file:///svn/repos/repo1

&amp;lt;?xml version="1.0"?&amp;gt;
&amp;lt;log&amp;gt;
svn: Failed to find time on revision 38307</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>Accès aux sources via le protocole HTTP</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">svn log --xml -v -r {2008-12-01T00:00:00Z}:{2012-&lt;wbr&gt;&lt;/wbr&gt;07-13T15:00:00Z} http://serveur/svn/

svn: Server sent unexpected return value (500 Internal Server Error) in response to
REPORT request for '/svn/repo1/!svn/vcc/&lt;wbr&gt;&lt;/wbr&gt;default'</code></pre>
</div>
</div>
<div class="paragraph">
<p>L&#8217;aperçu des informations sur les révisions SVN dans l&#8217;IDE Eclipse confirme également le problème :</p>
</div>
<div id="img-svnide" class="imageblock">
<div class="content">
<img src="https://mgreau.com/posts/images/posts/blog_eclipse_svn_logs1.png" alt="Révisions SVN dans IDE Eclipse">
</div>
<div class="title">Figure 1. Révisions SVN dans IDE Eclipse</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_solution">Solution</h2>
<div class="sectionbody">
<div class="paragraph">
<p>La solution consiste à ajouter une date sur <strong>les révisions en question.</strong></p>
</div>
<div class="paragraph">
<p>Pour cela, il faut commencer par <strong>autoriser la modification des propriétés</strong> sur le référentiel SVN puis par exécuter <strong>la commande <em>svn propset</em></strong> sur la révision concernée.</p>
</div>
<div class="sect2">
<h3 id="_autoriser_la_modification_des_propri_t_s_svn">Autoriser la modification des propriétés SVN</h3>
<div class="paragraph">
<p>Par défaut Apache Subversion définit un <a href="http://svnbook.red-bean.com/en/1.6/svn-book.html#svn.ref.reposhooks.pre-revprop-change"">hook (pre-revprop-change)</a> sur chaque référentiel SVN qui bloque la modification des propriétés SVN (hormis la propriété svn:log). il faut donc désactiver temporairement ce hook (faire une copie de sauvegarde avant)</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">echo '#!/bin/sh'&amp;nbsp; &amp;gt; repos/repo1/hooks/pre-revprop-change
echo 'exit 0'&amp;nbsp; &amp;nbsp; &amp;gt;&amp;gt; repos/repo1/hooks/pre-revprop-change
chmod 755 repos/repo1/hooks/pre-revprop-change</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_executer_la_commande_svn_pour_valoriser_les_propri_t_s_d_une_r_vision">Executer la commande SVN pour valoriser les propriétés d&#8217;une révision</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Il est donc possible d'éxécuter le commande SVN pour valoriser la propriété svn:date</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">svn propset -r38307  --revprop svn:date '2011-09-21T19:55:44.000220Z' file:///svn/repos/repo1
property 'svn:date' set on repository revision 38307</code></pre>
</div>
</div>
<div class="paragraph">
<p>On en profite également pour mettre à jour l&#8217;auteur du commit (svn:author) ainsi que le commentaire (svn:log) associé :</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">svn propset -r 38307 --revprop svn:author 'mgreau' file:///svn/repos/repo1
property 'svn:author' set on repository revision 38307</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">svn propset -r38307  --revprop svn:log 'Correction pb date de révision' file:///svn/repos/repo1
property 'svn:log' set on repository revision 38307</code></pre>
</div>
</div>
<div class="sect2">
<h3 id="_aper_u_du_r_sultat_dans_l_ide_eclipse">Aperçu du résultat dans l&#8217;IDE Eclipse</h3>
<div class="paragraph">
<p>En rafraichissant la vue <em>History</em> dans Eclipse, on s&#8217;aperçoit que la révision 38307 contient désormais des informations pour la date, l&#8217;auteur et le commentaire de commit.</p>
</div>
<div id="img-svnide2" class="imageblock">
<div class="content">
<img src="https://mgreau.com/posts/images/posts/blog_eclipse_svn_logs2.png" alt="Révisions SVN OK dans IDE Eclipse">
</div>
<div class="title">Figure 2. Révisions SVN OK dans IDE Eclipse</div>
</div>
</div>
<div class="sect2">
<h3 id="_script_de_mise_jour_automatique">Script de mise à jour automatique</h3>
<div class="paragraph">
<p>L&#8217;impression écran précédente met en valeur le fait que plusieurs révisions sont impactées par ce problème sur les propriétés SVN.
Je met à disposition <a href="https://docs.google.com/open?id=0Bx7rkna8etApWHJsbEJWY0hhdnM">un script shell (svn-propset.sh)</a> qui valorise ces propriétés SVN (date, auteur, commentaire) avec les valeurs passées en paramètres du script, pour un intervalle donné de révisions.</p>
</div>
<div class="paragraph">
<p><em>Exemple d&#8217;utilisation du script :</em></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">./svn-propset.sh -X -a mgreau -f 38215 -t 38321 -l "Correction pb date de révision"
 -d 2011-09-21T19:55:44.000220Z -u file:///svn/repos/repo1</code></pre>
</div>
</div>
<div class="paragraph">
<p>Pour d&#8217;autres informations vous pouvez consulter les posts (en anglais) qui m&#8217;ont permis de résoudre ce problème:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="http://docs.codehaus.org/display/HAUSMATES/Could+not+access+revision+times">http://docs.codehaus.org/display/HAUSMATES/Could+not+&lt;wbr&gt;&lt;/wbr&gt;access+revision+times</a></p>
</li>
<li>
<p><a href="http://blog.kymera-it.com/2011/02/changing-subversion-revision-times.html">http://blog.kymera-it.com/2011/02/changing-subversion-&lt;wbr&gt;&lt;/wbr&gt;revision-times.html</a></p>
</li>
</ul>
</div>
</div>
</div>
</div>]]></description><link>https://mgreau.com/posts/2012/07/30/corriger-les-proprietes-des-revisions-SVN.html</link><guid isPermaLink="true">https://mgreau.com/posts/2012/07/30/corriger-les-proprietes-des-revisions-SVN.html</guid><category><![CDATA[SVN]]></category><category><![CDATA[ Shell]]></category><dc:creator><![CDATA[Maxime Gréau]]></dc:creator><pubDate>Mon, 30 Jul 2012 00:00:00 GMT</pubDate></item></channel></rss>