<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>The developer's den</title>
    <description>We all live in the developer's den
</description>
    <link>http://bjansen.github.io/</link>
    <atom:link href="http://bjansen.github.io/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Wed, 03 Feb 2021 21:25:21 +0000</pubDate>
    <lastBuildDate>Wed, 03 Feb 2021 21:25:21 +0000</lastBuildDate>
    <generator>Jekyll v3.9.0</generator>
    
      <item>
        <title>Deploying to Maven Central using GitHub Actions</title>
        <description>&lt;p&gt;I have &lt;a href=&quot;https://github.com/bjansen/swagger-schema-validator/&quot;&gt;this library that I work on&lt;/a&gt; from time to time, that I published on Maven Central. A few days ago I released 
a new version, and since I always forget what the exact &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mvn&lt;/code&gt; command is, what my GPG passphrase is, etc. I thought it 
would be a good opportunity to automate the release process using &lt;a href=&quot;https://github.com/features/actions&quot;&gt;GitHub Actions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I came across several outdated tutorials that create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;settings.xml&lt;/code&gt; and import GPG keys manually, but as of Feb. 2021 most
of this is not needed anymore, thanks to &lt;a href=&quot;https://github.com/actions/setup-java/issues/43&quot;&gt;recent changes in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup-java&lt;/code&gt; action&lt;/a&gt;.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;From now on, I will assume that you already know how to &lt;a href=&quot;https://central.sonatype.org/pages/ossrh-guide.html&quot;&gt;deploy on Maven Central via Sonatype OSSRH&lt;/a&gt;. This means
you created an account on Sonatype’s Jira, your local &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;settings.xml&lt;/code&gt; is already configured and your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pom.xml&lt;/code&gt; has a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;plugins&amp;gt;&lt;/code&gt; section that looks more or less like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
        ...
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.sonatype.plugins&lt;span class=&quot;nt&quot;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;nexus-staging-maven-plugin&lt;span class=&quot;nt&quot;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.6.8&lt;span class=&quot;nt&quot;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;extensions&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/extensions&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;serverId&amp;gt;&lt;/span&gt;ossrh&lt;span class=&quot;nt&quot;&gt;&amp;lt;/serverId&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;nexusUrl&amp;gt;&lt;/span&gt;https://oss.sonatype.org/&lt;span class=&quot;nt&quot;&gt;&amp;lt;/nexusUrl&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;autoReleaseAfterClose&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/autoReleaseAfterClose&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
        ...
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;profiles&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;profile&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;release&lt;span class=&quot;nt&quot;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
                ...
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class=&quot;nt&quot;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-gpg-plugin&lt;span class=&quot;nt&quot;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.6&lt;span class=&quot;nt&quot;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;sign-artifacts&lt;span class=&quot;nt&quot;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;verify&lt;span class=&quot;nt&quot;&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                                &lt;span class=&quot;nt&quot;&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;sign&lt;span class=&quot;nt&quot;&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
                ...
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/profile&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/profiles&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;entering-github-actions&quot;&gt;Entering GitHub Actions&lt;/h2&gt;

&lt;p&gt;Java &lt;a href=&quot;https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions#workflows&quot;&gt;Actions workflows&lt;/a&gt; often use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup-java&lt;/code&gt; action which… well… sets up Java in the build runner:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;nn&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Set up JDK &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/setup-java@v1&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;java-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;11&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I thought this action was only used to download a JDK, but it turns out it can do more than that: it also knows
how to set up the runner to &lt;a href=&quot;https://github.com/actions/setup-java#publishing-using-apache-maven&quot;&gt;publish artifacts on Maven Central&lt;/a&gt; (or any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;distributionManagement&amp;gt;&lt;/code&gt; 
configured in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pom.xml&lt;/code&gt;, for that matter):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/checkout@v2&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Set up JDK &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.8&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/setup-java@v1&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;java-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.8&lt;/span&gt;

    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Build with Maven&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mvn -B package --file pom.xml&lt;/span&gt;

    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Set up Apache Maven Central&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/setup-java@v1&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# running setup-java again overwrites the settings.xml&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;java-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.8&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;server-id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;maven&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Value of the distributionManagement/repository/id field of the pom.xml&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;server-username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;MAVEN_USERNAME&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# env variable for username in deploy&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;server-password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;MAVEN_CENTRAL_TOKEN&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# env variable for token in deploy&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;gpg-private-key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Value of the GPG private key to import&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;gpg-passphrase&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;MAVEN_GPG_PASSPHRASE&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# env variable for GPG private key passphrase&lt;/span&gt;

    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Publish to Apache Maven Central&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mvn deploy&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;MAVEN_USERNAME&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;maven_username123&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;MAVEN_CENTRAL_TOKEN&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;MAVEN_GPG_PASSPHRASE&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;As explained in the readme, the second invocation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;actions/setup-java@v1&lt;/code&gt; will overwrite the runner’s 
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;settings.xml&lt;/code&gt; with your Sonatype credentials and GPG passphrase, using environment variables:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;settings&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://maven.apache.org/SETTINGS/1.0.0&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;xmlns:xsi=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.w3.org/2001/XMLSchema-instance&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;xsi:schemaLocation=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;servers&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;server&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;maven&lt;span class=&quot;nt&quot;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;username&amp;gt;&lt;/span&gt;${env.MAVEN_USERNAME}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/username&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;password&amp;gt;&lt;/span&gt;${env.MAVEN_CENTRAL_TOKEN}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/password&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/server&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;server&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;gpg.passphrase&lt;span class=&quot;nt&quot;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;passphrase&amp;gt;&lt;/span&gt;${env.MAVEN_GPG_PASSPHRASE}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/passphrase&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/server&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/servers&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/settings&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The private GPG key stored in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MAVEN_GPG_PRIVATE_KEY&lt;/code&gt; secret will also be imported in a GPG keychain, allowing
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maven-gpg-plugin&lt;/code&gt; to sign your artifacts correctly.&lt;/p&gt;

&lt;h2 id=&quot;adding-the-missing-pieces&quot;&gt;Adding the missing pieces&lt;/h2&gt;

&lt;p&gt;In order to make the workflow run smoothly, I needed two additional pieces of information that are not (yet) mentionned
in the readme:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;to fill the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MAVEN_GPG_PRIVATE_KEY&lt;/code&gt; secret, you need to &lt;a href=&quot;https://github.com/actions/setup-java/issues/100&quot;&gt;export your private GPG key using this command&lt;/a&gt;:
 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg --armor --export-secret-keys KEY_ID&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;to avoid a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg: signing failed: Inappropriate ioctl for device&lt;/code&gt; error, you need to &lt;a href=&quot;https://github.com/actions/setup-java/issues/83&quot;&gt;configure &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maven-gpg-plugin&lt;/code&gt;&lt;/a&gt;
like this:&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class=&quot;nt&quot;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-gpg-plugin&lt;span class=&quot;nt&quot;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.6&lt;span class=&quot;nt&quot;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Prevent gpg from using pinentry programs --&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;gpgArguments&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;arg&amp;gt;&lt;/span&gt;--pinentry-mode&lt;span class=&quot;nt&quot;&gt;&amp;lt;/arg&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;arg&amp;gt;&lt;/span&gt;loopback&lt;span class=&quot;nt&quot;&gt;&amp;lt;/arg&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/gpgArguments&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
    ...
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;final-note&quot;&gt;Final note&lt;/h2&gt;

&lt;p&gt;Setting up this workflow was not that difficult, except for the two missing pieces that I fortunately fixed very easily,
thanks to issues and comments from other people who had the same problem before me. 10/10, would recommend 😜.&lt;/p&gt;

&lt;p&gt;My final &lt;a href=&quot;https://github.com/bjansen/swagger-schema-validator/blob/master/.github/workflows/release.yml&quot;&gt;release workflow&lt;/a&gt; is available on GitHub.&lt;/p&gt;

</description>
        <pubDate>Wed, 03 Feb 2021 19:52:00 +0000</pubDate>
        <link>http://bjansen.github.io/java/2021/02/03/deploying-to-maven-central-using-github-actions.html</link>
        <guid isPermaLink="true">http://bjansen.github.io/java/2021/02/03/deploying-to-maven-central-using-github-actions.html</guid>
        
        
        <category>Java</category>
        
      </item>
    
      <item>
        <title>High performance IBM MQ &amp; JMS applications</title>
        <description>&lt;p&gt;Using the JMS API to do messaging over IBM MQ is rather easy, but writing programs that 
perform well can be a bit tricky. In this post, I’m going to share a few tips I’ve gathered here
and there that might help you write faster MQ+JMS applications.&lt;/p&gt;

&lt;p&gt;My examples are based on MQ 9 and JMS 2.0 but most of the tips will likely work with
other versions of these products, and some of them can even apply to other message brokers.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h2 id=&quot;tuning-the-client-part&quot;&gt;Tuning the client part&lt;/h2&gt;

&lt;h3 id=&quot;cache-stuff&quot;&gt;Cache stuff&lt;/h3&gt;

&lt;p&gt;The first thing to do is make sure that you are reusing JMS objects as much as possible. 
Creating a connection or a session usually requires a network exchange, which means it can
introduce a significant overhead. While the creation of producers and consumers has less
overhead, it’s good to know that these objets were &lt;em&gt;designed to be reused&lt;/em&gt;, so you might as
well take advantage of this and cache/reuse instances as much as possible.&lt;/p&gt;

&lt;p&gt;If you are using Spring JMS, &lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jms/connection/CachingConnectionFactory.html&quot;&gt;CachingConnectionFactory&lt;/a&gt;
can wrap your connection factory to reuse a single connection, cache sessions and producers.&lt;/p&gt;

&lt;p&gt;If your code is running in a Java EE application server, chances are that &lt;a href=&quot;https://developer.jboss.org/wiki/ShouldICacheJMSConnectionsAndJMSSessions?_sscc=t&quot;&gt;connections and sessions
are already automatically cached&lt;/a&gt;,
and calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;close()&lt;/code&gt; on one of these objects will put it back in a pool.&lt;/p&gt;

&lt;h3 id=&quot;use-non-persistent-messages&quot;&gt;Use non-persistent messages&lt;/h3&gt;

&lt;p&gt;Performance often comes at the cost of reliability. Depending on the requirements your
application has, it can be acceptable to lose messages if the queue manager crashes. In that
case, your application might expect a response to the lost message, not receive it and decide
to send it again. This kind of scenario is most likely privileging performance over reliability,
while still having a plan B in case something doesn’t go as expected.&lt;/p&gt;

&lt;p&gt;To improve performance in such scenarios, it is best to use non-persistent messages. Persistent
messages are used by queue managers to &lt;em&gt;guarantee message delivery&lt;/em&gt;. In practice, this means that a
message will be synchronously written to disk &lt;em&gt;at the same time it is sent&lt;/em&gt; by the producer. As you can guess, the
previous sentence makes it clear that persistent messages impact performance.&lt;/p&gt;

&lt;p&gt;Now, to debunk a common misconception: &lt;strong&gt;there is no such thing as a persistent queue&lt;/strong&gt;. Persistency
is configured &lt;em&gt;per message&lt;/em&gt;, and if a given message has no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Persistence&lt;/code&gt; field set in its message
descriptor (MQMD), then &lt;a href=&quot;https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.0.0/com.ibm.mq.dev.doc/q023070_.htm&quot;&gt;a queue can define a &lt;strong&gt;default&lt;/strong&gt; persistence flag&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To send non persistent messages, &lt;a href=&quot;https://docs.oracle.com/cd/E19798-01/821-1841/bncfy/index.html&quot;&gt;specify a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DeliveryMode&lt;/code&gt; when using a producer&lt;/a&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// on the whole producer&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setDeliveryMode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DeliveryMode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;NON_PERSISTENT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// on a single message&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DeliveryMode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;NON_PERSISTENT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Be aware that &lt;a href=&quot;https://docs.oracle.com/javaee/7/api/javax/jms/MessageProducer.html#setDeliveryMode-int-&quot;&gt;JMS’ default mode is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DeliveryMode.PERSISTENT&lt;/code&gt;&lt;/a&gt;,
which means that even if you define your queue’s default persistence as “not persistent”, 
JMS will not care and send persistent messages when calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;producer.send(message)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you are using &lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jms/core/JmsTemplate.html&quot;&gt;Spring JMS’ JmsTemplate&lt;/a&gt;,
you can enable explicit QOS and set a default delivery mode:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@Bean&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;JmsTemplate&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;jmsTemplate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NamingException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;JmsTemplate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jmsTemplate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;JmsTemplate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;jmsTemplate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setConnectionFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queueConnectionFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;jmsTemplate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setExplicitQosEnabled&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;jmsTemplate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setDeliveryPersistent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jmsTemplate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If you are using &lt;a href=&quot;https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.0.0/com.ibm.mq.con.doc/q123210_.htm&quot;&gt;a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.bindings&lt;/code&gt; file to look up JMS objects from a JNDI context&lt;/a&gt;,
you can configure JMSAdmin to set a default delivery mode on producers using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;persistence&lt;/code&gt; property:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;define q(MY.JNDI.Q) queue(MY_Q) qmgr(MY_QMGR) persistence(NON)&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;lose-the-transaction&quot;&gt;Lose the transaction&lt;/h3&gt;

&lt;p&gt;JMS &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Session&lt;/code&gt;s can be transactional, but this has a cost. If your application does not need to do
multiple operations in a single atomic transaction, then you can make your code a bit faster using
non-transactional sessions:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// transactions are configured using the first parameter&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;Session&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;createQueueSession&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AUTO_ACKNOWLEDGE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;change-acknowledgement-modes&quot;&gt;Change acknowledgement modes&lt;/h3&gt;

&lt;p&gt;When using non-transactional sessions, the queue manager has to find an alternative to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;commit()&lt;/code&gt; to make
sure that a message was successfully consumed by a client application. This is achieved using aknowledgements.
When a message is acknowledged, the queue manager knows that the delivery has been guaranteed and that this message 
can be discarded. JMS supports several acknowledgement modes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;In the default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AUTO_ACKNOWLEDGE&lt;/code&gt; mode, a message will be automatically acknowledged when
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;consumer.receive()&lt;/code&gt; returns a message, of when a &lt;a href=&quot;https://docs.oracle.com/javaee/7/api/javax/jms/MessageListener.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MessageListener&lt;/code&gt;&lt;/a&gt;
was successfully executed on a message.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLIENT_ACKNOWLEDGE&lt;/code&gt; mode, you have to manually call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;message.acknowledge()&lt;/code&gt;. If your application does
more work after having received a message, you could acknowledge it immediately to relieve the queue manager of its duty
and allow it to process the next message.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DUPS_OK_ACKNOWLEDGE&lt;/code&gt; mode, you tell the queue manager that it’s okay for your application to reveive the same
message more than once. Now that the ‘once and only once’ constraint is gone, the queue manager might take a few shortcuts
to enhance performance, which could lead to a message being sent twice on rare occasions.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In theory, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DUPS_OK_ACKNOWLEDGE&lt;/code&gt; should be the most performant mode, but in practice it means that you have to design
your application to support duplicate messages. This could add complexity on multi-threaded or multi-instances applications.&lt;/p&gt;

&lt;h3 id=&quot;use-read-ahead-and-put-async&quot;&gt;Use read ahead and put async&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.0.0/com.ibm.mq.dev.doc/q032570_.htm&quot;&gt;“Read ahead”&lt;/a&gt; 
is a concept specific to IBM MQ. When this functionality is enabled and you are using &lt;em&gt;non-transactional sessions&lt;/em&gt;,
non-persistent messages can be pre-fetched in an internal buffer so that your application can consume the next message faster.&lt;/p&gt;

&lt;p&gt;When you call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;receive()&lt;/code&gt; method, the IBM MQ classes for JMS will fetch a list of messages, store them in
this internal buffer, and return the first message to your application. The next calls to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;receive()&lt;/code&gt; will then take
messages from this buffer instead of querying the queue manager. This can work because no transactions and non-persistent 
messages are involved, which means there is no hard requirement for guaranteed delivery and consistency.&lt;/p&gt;

&lt;p&gt;Read ahead can be enabled on a queue like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;MQQueue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;myQueue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setReadAheadAllowed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;CommonConstants&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;WMQ_READ_AHEAD_ALLOWED_ENABLED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Similar to read ahead that allows faster &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MQGET&lt;/code&gt;s, IBM MQ has a concept of &lt;a href=&quot;https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.0.0/com.ibm.mq.dev.doc/q032560_.htm&quot;&gt;“put async”&lt;/a&gt; that allows faster &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MQPUT&lt;/code&gt;s.
In normal conditions, the queue manager sends back a confirmation that the request was processed. In put async mode,
your application can put the next message without having to wait for this confirmation. This can be useful for
custom injectors, in which you might want to inject messages as fast as possible.&lt;/p&gt;

&lt;p&gt;Put async can be enabled on a queue like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;MQQueue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;myQueue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setPutAsyncAllowed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;CommonConstants&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;WMQ_PUT_ASYNC_ALLOWED_ENABLED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;use-bindings-mode&quot;&gt;Use bindings mode&lt;/h3&gt;

&lt;p&gt;IBM MQ supports two transport modes, i.e. two ways to connect a client application to a queue manager.
The most common one is probably the client mode, which uses TCP/IP connections to connect to a server channel.
They can be fast on loopback interfaces, but can easily make your application feel “slower” on physical
network interfaces.&lt;/p&gt;

&lt;p&gt;The second mode is called &lt;a href=&quot;https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.0.0/com.ibm.mq.dev.doc/q031720_.htm&quot;&gt;“bindings mode”&lt;/a&gt;
(not to be confused with &lt;a href=&quot;https://www.ibm.com/support/knowledgecenter/en/prodconn_1.0.0/com.ibm.scenarios.wmqwasha.doc/topics/ins_mq_JNDI.htm?cp=SSFKSJ_9.0.0&quot;&gt;JNDI bindings&lt;/a&gt;!).
In bindings mode, the client application uses JNI to connect to the queue manager directly using &lt;a href=&quot;https://en.wikipedia.org/wiki/Inter-process_communication&quot;&gt;IPC&lt;/a&gt;.
This means that your application &lt;em&gt;must&lt;/em&gt; run on the same machine as the one running the queue manager, because it will be working
“in memory” instead of using network connections.&lt;/p&gt;

&lt;p&gt;The transport mode has to be configured on the connection factory:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;nc&quot;&gt;MQConnectionFactory&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qcf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MQQueueConnectionFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// no need to specify a host/port/channel here&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;qcf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setTransportType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;CommonConstants&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;WMQ_CM_BINDINGS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;tuning-the-server-part&quot;&gt;Tuning the server part&lt;/h2&gt;

&lt;p&gt;Before mentioning what can be configured on the server side, here’s a  reminder of the terminology related to MQ connections.
An MQ ‘server’ consists of one or more &lt;em&gt;queue managers&lt;/em&gt; (QM), each one managing one or more &lt;em&gt;queues&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;To access a queue from a JMS application, you have to get a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Connection&lt;/code&gt; to the QM using a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QueueConnectionFactory&lt;/code&gt;. 
When the ‘client’ transport is used (i.e. TCP/IP connections), connections are made to a specific &lt;a href=&quot;https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.0.0/com.ibm.mq.explorer.doc/e_channels.htm&quot;&gt;&lt;em&gt;channel&lt;/em&gt;&lt;/a&gt;. When an application connects
to a channel, the QM creates what is called a &lt;em&gt;channel instance&lt;/em&gt;.&lt;/p&gt;

&lt;h3 id=&quot;increase-the-number-of-channel-instances&quot;&gt;Increase the number of channel instances&lt;/h3&gt;

&lt;p&gt;One way to increase the throughput of your application is to listen for incoming messages in more than one thread.
This can be easily achieved in Spring JMS using &lt;a href=&quot;https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jms/listener/DefaultMessageListenerContainer.html#setConcurrency-java.lang.String-&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;concurrency&lt;/code&gt;&lt;/a&gt;,
but you should be aware that there are several parameters in IBM MQ that control (limit) the number of network connections 
(channel instances) client applications can make.&lt;/p&gt;

&lt;p&gt;A channel can define two parameters related to &lt;a href=&quot;https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.0.0/com.ibm.mq.con.doc/q015640_.htm&quot;&gt;channel instances&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MAXINST&lt;/code&gt;: maximum number of channel instances&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MAXINSTC&lt;/code&gt;: maximum number of channel instances &lt;em&gt;per client application&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These two parameters can be easily viewed and modified using MQ Explorer, in the channel properties page.&lt;/p&gt;

&lt;p&gt;A &lt;a href=&quot;https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.0.0/com.ibm.mq.ref.dev.doc/q102490_.htm&quot;&gt;third —less obvious— parameter&lt;/a&gt; can be configured at the queue manager level:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MaxChannels&lt;/code&gt;: maximum number of channel instances all channels combined&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By default, 100 channel instances (network sockets) are allowed on a given queue manager. As we will see in the next section, 
it is important to be aware of this parameter. To change it, add/modify the following section in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/mqm/qmgrs/MyQueueManager/qm.ini&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;Channels:
    MaxChannels=200&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If a channel defines &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MaxInst=200&lt;/code&gt; but the QM defines &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MaxChannels=100&lt;/code&gt;, then only 100 channel instances will be allowed.&lt;/p&gt;

&lt;h3 id=&quot;dont-share-conversations&quot;&gt;Don’t share conversations&lt;/h3&gt;

&lt;p&gt;IBM MQ has a notion of &lt;a href=&quot;https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.0.0/com.ibm.mq.dev.doc/q032530_.htm&quot;&gt;“shared conversations”&lt;/a&gt;, 
meaning that multiple JMS connections/sessions can be shared on the same channel instance (TCP/IP connection). By default, this parameter is set to 10, 
but you can change the value in MQ Explorer in the channel properties page. Sharing conversations means that less network sockets will be used, 
which can sometimes be a good thing because it removes the overhead introduced each time a new network connection is opened. On the other end, this 
can also have a performance cost on applications that do long running operations on the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Session&lt;/code&gt;, like message listeners, because data from different 
sessions has to be ‘mixed’ in a same socket, then ‘unmixed’ on the other end.&lt;/p&gt;

&lt;p&gt;Setting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHARECNV&lt;/code&gt; to 1 can improve performance dramatically in some cases, because the channel instance will be dedicated to a given &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Session&lt;/code&gt;.
Beware though that it can lead to ‘connection refused’ errors very fast. As we saw in the previous section, queue managers only allow 100 channel 
instances by default. Assuming you don’t share conversations and your application is the only one using that queue manager, this means that if you 
start 50 consumers on 2 queues, you will end up with 1 JMS connection + 2*50 sessions = 101 channel instances, so the last thread won’t be able 
to start correctly.&lt;/p&gt;

&lt;p&gt;When using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHARECNV=1&lt;/code&gt;, it is recommended to carefully determine the number of channel instances that will be used by your application,
and adapt the parameters we saw in the previous section accordingly.&lt;/p&gt;

&lt;p&gt;Of course, because shared conversations and channel instances apply to &lt;em&gt;network connections&lt;/em&gt;, you don’t have to worry about all these
parameters when using ‘bindings’ transport.&lt;/p&gt;

&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://www.precisejava.com/javaperf/j2ee/JMS.htm&quot;&gt;Best practices to improve performance in JMS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;ftp://public.dhe.ibm.com/software/solutions/soa/pdfs/IBM_WebShere_MQ_Performance_Report.pdf&quot;&gt;High performance messaging&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www2.sys-con.com/itsg/virtualcd/Java/archives/0604/chappell/index.html&quot;&gt;Guaranteed messaging with JMS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.ibm.com/developerworks/community/blogs/messaging/entry/programming_a_websphere_mq_jms_client_for_performance7?lang=en&quot;&gt;Programming a WebSphere MQ JMS Client for performance&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.ibm.com/developerworks/community/blogs/aimsupport/entry/avoiding_runaway_numbers_of_mq_channels?lang=en&quot;&gt;Avoiding run-away numbers of channels&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 04 Mar 2018 21:56:00 +0000</pubDate>
        <link>http://bjansen.github.io/java/2018/03/04/high-performance-mq-jms.html</link>
        <guid isPermaLink="true">http://bjansen.github.io/java/2018/03/04/high-performance-mq-jms.html</guid>
        
        
        <category>Java</category>
        
      </item>
    
      <item>
        <title>Ceylon + jOOQ = &amp;lt;3</title>
        <description>&lt;p&gt;So you want to try Ceylon on a new side project, but don’t feel like writing yet another boring command line application? In this article, I will show you how to set up a new Ceylon application that uses jOOQ seamlessly. We will see how easy it is to use Ceylon’s Java interoperability, and how easy it is to customize jOOQ and adapt it to Ceylon’s conventions. The sample application will use an existing database.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h2 id=&quot;creating-a-new-project&quot;&gt;Creating a new project&lt;/h2&gt;

&lt;p&gt;The first step is to create a project in &lt;a href=&quot;http://ceylon-lang.org/documentation/ide/&quot;&gt;Ceylon IDE&lt;/a&gt;. We will create two Ceylon modules:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gen.example.jooq&lt;/code&gt; will contain classes generated by jOOQ’s &lt;a href=&quot;http://www.jooq.org/doc/3.6/manual/code-generation/&quot;&gt;code generator&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;example.jooq&lt;/code&gt; will contain “actual” code that uses those generated classes to query our existing database&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;adding-dependencies-to-jooq&quot;&gt;Adding dependencies to jOOQ&lt;/h3&gt;

&lt;p&gt;Since jOOQ provides Maven dependencies, we will use Ceylon’s Maven support. In the project settings, under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ceylon Build &amp;gt; Module Repositories&lt;/code&gt; we will use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Add Maven Repository...&lt;/code&gt; to configure our local &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m2&lt;/code&gt; repo.&lt;/p&gt;

&lt;p&gt;Now we can add dependencies to jOOQ, to Java and to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ceylon.interop.java&lt;/code&gt; since the code will be generated in Java:&lt;/p&gt;

&lt;pre&gt;&lt;code data-language=&quot;ceylon&quot;&gt;
module gen.example.jooq &quot;1.0.0&quot; {
    shared import &quot;org.jooq:jooq&quot; &quot;3.6.0&quot;;
    shared import &quot;org.jooq:jooq-meta&quot; &quot;3.6.0&quot;;
    import &quot;org.jooq:jooq-codegen&quot; &quot;3.6.0&quot;;
    
    shared import java.base &quot;8&quot;;
    import java.jdbc &quot;8&quot;;
    import javax.annotation &quot;8&quot;;
    import ceylon.interop.java &quot;1.1.1&quot;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Some of the imports are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shared&lt;/code&gt; because they will be reused in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;example.jooq&lt;/code&gt;, whereas the others are here for internal use only. The other &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;module.ceylon&lt;/code&gt; will look like this:&lt;/p&gt;

&lt;pre&gt;&lt;code data-language=&quot;ceylon&quot;&gt;
module example.jooq &quot;1.0.0&quot; {
    import gen.example.jooq &quot;1.0.0&quot;;

    import &quot;mysql:mysql-connector-java&quot; &quot;5.1.36&quot;;
    import &quot;com.zaxxer:HikariCP-java6&quot; &quot;2.3.9&quot;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that we also import a MySQL driver and &lt;a href=&quot;http://brettwooldridge.github.io/HikariCP/&quot;&gt;HikariCP&lt;/a&gt; to connect to our MySQL database.&lt;/p&gt;

&lt;h3 id=&quot;generating-classes&quot;&gt;Generating classes&lt;/h3&gt;

&lt;p&gt;We will use MySQL’s &lt;a href=&quot;http://dev.mysql.com/doc/sakila/en/sakila-installation.html&quot;&gt;Sakila sample database&lt;/a&gt;. Once the database is downloaded and installed, generating our model classes consists of creating a configuration file named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jooq-config.xml&lt;/code&gt;, then invoking &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org.jooq.util.GenerationTool&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here’s how &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jooq-config.xml&lt;/code&gt; looks like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;yes&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.jooq.org/xsd/jooq-codegen-3.6.0.xsd&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Configure the database connection here --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;jdbc&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;driver&amp;gt;&lt;/span&gt;com.mysql.jdbc.Driver&lt;span class=&quot;nt&quot;&gt;&amp;lt;/driver&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;url&amp;gt;&lt;/span&gt;jdbc:mysql://localhost:3306/sakila&lt;span class=&quot;nt&quot;&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;user&amp;gt;&lt;/span&gt;root&lt;span class=&quot;nt&quot;&gt;&amp;lt;/user&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;password&amp;gt;&amp;lt;/password&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/jdbc&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;generator&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;org.jooq.util.DefaultGenerator&lt;span class=&quot;nt&quot;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
    
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;database&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;org.jooq.util.mysql.MySQLDatabase&lt;span class=&quot;nt&quot;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;inputSchema&amp;gt;&lt;/span&gt;sakila&lt;span class=&quot;nt&quot;&gt;&amp;lt;/inputSchema&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;includes&amp;gt;&lt;/span&gt;.*&lt;span class=&quot;nt&quot;&gt;&amp;lt;/includes&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/database&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;target&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;packageName&amp;gt;&lt;/span&gt;gen.example.jooq&lt;span class=&quot;nt&quot;&gt;&amp;lt;/packageName&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;directory&amp;gt;&lt;/span&gt;./source&lt;span class=&quot;nt&quot;&gt;&amp;lt;/directory&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/target&amp;gt;&lt;/span&gt;
    
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;generate&amp;gt;&lt;/span&gt;
    	&lt;span class=&quot;nt&quot;&gt;&amp;lt;pojos&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/pojos&amp;gt;&lt;/span&gt;
    	&lt;span class=&quot;nt&quot;&gt;&amp;lt;daos&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/daos&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/generate&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/generator&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The important thing to configure is the output directory: files will be generated in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source&lt;/code&gt; directory, under the package &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gen.example.jooq&lt;/code&gt;, this way they will be automatically included in the Ceylon module:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ceylon-jooq/gen-files.png&quot; width=&quot;298&quot; height=&quot;379&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The only missing pieces are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.ceylon&lt;/code&gt; files that are needed in each subpackage, otherwise Ceylon’s type checker will not recognize them. It’s very easy to create them by hand, and they won’t be erased the next time we invoke the code generator.&lt;/p&gt;

&lt;h2 id=&quot;querying-the-database&quot;&gt;Querying the database&lt;/h2&gt;

&lt;p&gt;We are now ready to produce actual code to query the database. To do that, we need to configure a data source. We will be using an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object&lt;/code&gt;, since this is obviously a singleton:&lt;/p&gt;

&lt;pre&gt;&lt;code data-language=&quot;ceylon&quot;&gt;
object dataSource extends HikariDataSource() {
    
    shared void setup() {
        value mysqlDS = MysqlDataSource();
        mysqlDS.databaseName = &quot;sakila&quot;;
        mysqlDS.user = &quot;root&quot;;
        mysqlDS.setPassword(&quot;&quot;);
        
        dataSource = mysqlDS;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can now query the database, for example the table &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Actors&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code data-language=&quot;ceylon&quot;&gt;
shared void run() {
    dataSource.setup();
    
    value dsl = DSL.using(dataSource, SQLDialect.\iMYSQL);
    
    Result&amp;lt;ActorRecord&amp;gt; actors = dsl.selectFrom(Actor.\iACTOR).fetch();
    
    for (actor in CeylonIterable(actors)) {
        print(&quot;Actor ``actor.firstName`` ``actor.lastName``&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can easily iterate over a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Result&amp;lt;ActorRecord&amp;gt;&lt;/code&gt; because it implements &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;java.lang.Iterable&lt;/code&gt;, which can be adapted to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ceylon.language.Iterable&lt;/code&gt; using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CeylonIterable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s see how more complex cases work:&lt;/p&gt;

&lt;pre&gt;&lt;code data-language=&quot;ceylon&quot;&gt;
value overdueRentals = dsl.select(
        concat(customer.\iLAST_NAME, 
               DSL.val(javaString(&quot; &quot;)),
               customer.\iFIRST_NAME).as(&quot;customer&quot;),
        address.\iPHONE, film.\iTITLE
    )
    .from(rental)
    .join(customer).on(rental.\iCUSTOMER_ID.eq(customer.\iCUSTOMER_ID))
    .join(address).on(customer.\iADDRESS_ID.eq(address.\iADDRESS_ID))
    .join(inventory).on(rental.\iINVENTORY_ID.eq(inventory.\iINVENTORY_ID))
    .join(film).on(inventory.\iFILM_ID.eq(film.\iFILM_ID))
    .where(rental.\iRETURN_DATE.isNull())
    .and(
        timestampAdd(rental.\iRENTAL_DATE, film.\iRENTAL_DURATION, DatePart.\iDAY)
        .lt(currentTimestamp())
    )
    .limit(5)
    .fetch();
    
for (rental in CeylonIterable(overdueRentals)) {
    print(&quot;Customer ``rental.value1()``, phone ``rental.value2()``,
            title ``rental.value3()``&quot;);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This time, I used import aliases to transform things like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rental.\IRENTAL&lt;/code&gt; into a user-friendly &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rental&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code data-language=&quot;ceylon&quot;&gt;
import gen.example.jooq.tables {
    Actor,
    Customer {
        customer = \iCUSTOMER
    },
    Address {
        address = \iADDRESS
    },
    ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;making-things-more-ceylon-friendly&quot;&gt;Making things more Ceylon-friendly&lt;/h2&gt;

&lt;p&gt;Now that we know jOOQ can be easily used from Ceylon, let’s see how we can improve things a little.&lt;/p&gt;

&lt;h3 id=&quot;getting-rid-of-i&quot;&gt;Getting rid of \i&lt;/h3&gt;
&lt;p&gt;The Ceylon specification indicates that &lt;a href=&quot;http://ceylon-lang.org/documentation/spec/html/typesystem.html#identifiernaming&quot;&gt;properties &lt;em&gt;must&lt;/em&gt; begin with a lowercase character&lt;/a&gt;, so when we try to access &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Actor.ACTOR&lt;/code&gt;, we actually have to escape it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Actor.\iACTOR&lt;/code&gt;. We saw that it can be worked around using import aliases, but it becomes tedious if we also have to alias fields like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Actor.ACTOR.ACTOR_ID&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;jOOQ provides nice extension points in its code generator, one of them is &lt;a href=&quot;http://www.jooq.org/doc/3.6/manual/code-generation/codegen-generatorstrategy/&quot;&gt;custom generator strategies&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;[Generator strategies] can be very useful if you want to inject custom behaviour into jOOQ’s code generator with respect to naming classes, members, methods, and other Java objects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is exactly what we are looking for, we just have to override the default strategy to generate field names in camelCase:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CamelCaseStrategy&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DefaultGeneratorStrategy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getJavaIdentifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Definition&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;definition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;org&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;jooq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;StringUtils&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toCamelCaseLC&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;definition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getOutputName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And use it in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jooq-config.xml&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;generator&amp;gt;&lt;/span&gt;
    ...
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;strategy&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;example.jooq.CamelCaseStrategy&lt;span class=&quot;nt&quot;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/strategy&amp;gt;&lt;/span&gt;
    ...
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/generator&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;After having regenerated our classes, the previous sample code feels more natural and ceylonic:&lt;/p&gt;

&lt;pre&gt;&lt;code data-language=&quot;ceylon&quot;&gt;
...
.from(rental)
.join(customer).on(rental.customerId.eq(customer.customerId))
.join(address).on(customer.addressId.eq(address.addressId))
.join(inventory).on(rental.inventoryId.eq(inventory.inventoryId))
.join(film).on(inventory.filmId.eq(film.filmId))
.where(rental.returnDate.isNull())
...
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;using-ceylon-types-ceylonlanguage&quot;&gt;Using Ceylon types (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ceylon.language.*&lt;/code&gt;)&lt;/h3&gt;

&lt;p&gt;You may have noticed that things can get ugly when we have to “force” the use of a Java type instead of a Ceylon type, for example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;String&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code data-language=&quot;ceylon&quot;&gt;
dsl.select(
    concat(customer.lastName, 
           val(javaString(&quot; &quot;)),
           customer.firstName)
)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;javaString&lt;/code&gt; is a function that transforms a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ceylon.language.String&lt;/code&gt; into a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;java.lang.String&lt;/code&gt;. Without it, jOOQ won’t recognize Ceylon types and instead will throw a nasty&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;Type class ceylon.language.String is not supported in dialect DEFAULT&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Luckily for us, jOOQ also allows us to use our very own &lt;a href=&quot;http://www.jooq.org/doc/3.6/manual/sql-execution/fetching/data-type-conversion/&quot;&gt;custom types&lt;/a&gt; during &lt;a href=&quot;http://www.jooq.org/doc/3.6/manual/code-generation/custom-data-types/&quot;&gt;code generation&lt;/a&gt;. We just have to implement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org.jooq.Converter&lt;/code&gt;, then configure when to use this converter. Let’s see how we can map SQL strings to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ceylon.language.String&lt;/code&gt; instances:&lt;/p&gt;

&lt;pre&gt;&lt;code data-language=&quot;ceylon&quot;&gt;
import java.lang {
    JString = String,
}

shared class StringConverter() satisfies Converter&amp;lt;JString, String&amp;gt; {
    shared actual String? from(JString? t) =&amp;gt; if (exists t) then t.string else null;
    
    shared actual Class&amp;lt;JString&amp;gt; fromType() =&amp;gt; javaClass&amp;lt;JString&amp;gt;();
    
    shared actual JString? to(String? u) =&amp;gt; if (exists u) then javaString(u) else null;
    
    shared actual Class&amp;lt;String&amp;gt; toType() =&amp;gt; javaClass&amp;lt;String&amp;gt;();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Next, we have to let the generator know that we would like to use this converter for every string in the database (in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jooq-config.xml&lt;/code&gt;):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;database&amp;gt;&lt;/span&gt;
 ...
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;customTypes&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;customType&amp;gt;&lt;/span&gt;
       &lt;span class=&quot;nt&quot;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;CString&lt;span class=&quot;nt&quot;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
       &lt;span class=&quot;nt&quot;&gt;&amp;lt;type&amp;gt;&lt;/span&gt;ceylon.language.String&lt;span class=&quot;nt&quot;&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
       &lt;span class=&quot;nt&quot;&gt;&amp;lt;converter&amp;gt;&lt;/span&gt;gen.example.jooq.StringConverter
       &lt;span class=&quot;nt&quot;&gt;&amp;lt;/converter&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/customType&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;/customTypes&amp;gt;&lt;/span&gt;
   
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;forcedTypes&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;forcedType&amp;gt;&lt;/span&gt;
       &lt;span class=&quot;nt&quot;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;CString&lt;span class=&quot;nt&quot;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
       &lt;span class=&quot;nt&quot;&gt;&amp;lt;types&amp;gt;&lt;/span&gt;varchar&lt;span class=&quot;nt&quot;&gt;&amp;lt;/types&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/forcedType&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;/forcedTypes&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/database&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;varchar&lt;/code&gt; column will now be mapped to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ceylon.lang.String&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ceylon-jooq/fields-as-ceylon-string.png&quot; width=&quot;590&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This can then be repeated for integers, datetimes, etc.&lt;/p&gt;

&lt;h3 id=&quot;gotcha&quot;&gt;Gotcha&lt;/h3&gt;

&lt;p&gt;Until version 3.6.x, jOOQ forces us to use an &lt;a href=&quot;http://www.jooq.org/doc/3.6/manual/sql-execution/crud-with-updatablerecords/identity-values/&quot;&gt;identity&lt;/a&gt; that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extends java.lang.Number&lt;/code&gt;. This has two consequences for us:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;if we replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;java.lang.Integer&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ceylon.language.Integer&lt;/code&gt;, the generated table classes will not compile because&lt;/p&gt;

    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Identity&amp;lt;R, ? extends Number&amp;gt; getIdentity()&lt;/code&gt;&lt;/p&gt;

    &lt;p&gt;will be overriden with&lt;/p&gt;

    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Identity&amp;lt;R, ceylon.language.Integer&amp;gt; getIdentity()&lt;/code&gt;&lt;/p&gt;

    &lt;p&gt;and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ceylon.language.Integer&lt;/code&gt; does not extend &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Number&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;if we delete those overriden methods, everything will compile and will mostly run as expected, except for a few cases like &lt;a href=&quot;https://groups.google.com/forum/#!topic/jooq-user/uPBvwmx-IQQ&quot;&gt;this one&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fortunately, this will be fixed in &lt;a href=&quot;https://github.com/jOOQ/jOOQ/issues/4488&quot;&gt;jOOQ 3.7.0&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;This article showed that integrating jOOQ in your Ceylon project requires very little effort, thanks to jOOQ that is flexible enough to let us customize naming strategies and data types. This also proves that the Java interop provided by Ceylon works very well and allows us to reuse existing Java frameworks.&lt;/p&gt;

&lt;p&gt;The source code for this article is available in a &lt;a href=&quot;https://github.com/bjansen/ceylon-jooq-example&quot;&gt;sample project&lt;/a&gt; on GitHub.&lt;/p&gt;

&lt;p&gt;If you plan on integrating jOOQ in your own Ceylon project, I also started &lt;a href=&quot;https://github.com/bjansen/ceylon-jooq-adapter&quot;&gt;ceylon-jooq-adapter&lt;/a&gt; which allows easy integration of jOOQ in your Ceylon project. It contains what was covered in this article, but other features will be added in the future.&lt;/p&gt;
</description>
        <pubDate>Mon, 24 Aug 2015 19:25:00 +0000</pubDate>
        <link>http://bjansen.github.io/ceylon/2015/08/24/ceylon-plus-jooq-equals-heart.html</link>
        <guid isPermaLink="true">http://bjansen.github.io/ceylon/2015/08/24/ceylon-plus-jooq-equals-heart.html</guid>
        
        
        <category>Ceylon</category>
        
      </item>
    
      <item>
        <title>Monitorer ses logs avec Logstash</title>
        <description>&lt;p&gt;Lundi matin, aux environs de 9 h 30. Vous arrivez un peu en retard, mais bon c’était pour la bonne cause, il fallait impérativement finir de merger ces pull requests sur votre projet perso et vous en avez eu jusqu’à 3h du mat’. Même pas le temps d’aller prendre une boisson chaude pour se réveiller, le téléphone sonne : une anomalie ultra-bloquante-qui-va-mettre-en-péril-le-projet-c’est-pas-possible vient d’être ouverte, c’est la catastrophe bla bla bla… Classique, me direz-vous, mais comment trouver facilement l’origine du problème ?&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h1 id=&quot;les-logs-bien-sûr-&quot;&gt;Les logs, bien sûr !&lt;/h1&gt;

&lt;p&gt;Comme d’habitude, le ticket a été ouvert à l’arrache, il va falloir batailler ne serait-ce que pour avoir une heure approximative à laquelle le problème s’est produit. Vous vous connectez en SSH sur la machine de prod, lancez un &lt;code&gt;vim + /var/log/jboss/server.log&lt;/code&gt; et c’est parti pour la recherche ! Ah, en fait il y a eu plein de stacktraces depuis minuit, c’est dingue ça… et évidemment, c’est pas très lisible. Bon, depuis combien de temps le problème existe ? Il va falloir faire un &lt;code&gt;grep&lt;/code&gt; sur tous les logs pour voir si l’erreur a déjà été rencontrée auparavant. Et puis tant qu’à faire, autant jeter un œil à toutes ces autres erreurs qu’on a trouvées par hasard…&lt;/p&gt;

&lt;h1 id=&quot;un-analyseur-de-logs-à-la-rescousse&quot;&gt;Un analyseur de logs à la rescousse&lt;/h1&gt;

&lt;p&gt;Mieux vaut prévenir que guérir&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; . L’idéal serait d’avoir un outil qui scrute automatiquement les logs à la recherche de &lt;code&gt;WARN&lt;/code&gt; et d’&lt;code&gt;ERROR&lt;/code&gt;. Il existe pas mal d’outils dans ce genre, comme quoi le problème est bien reconnu et adressé : &lt;a href=&quot;https://stackhunter.com/&quot;&gt;Stack Hunter&lt;/a&gt;&lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, &lt;a href=&quot;http://www.baremetalsoft.com/baretail/&quot;&gt;Baretail&lt;/a&gt; ou encore &lt;a href=&quot;http://logstash.net/&quot;&gt;Logstash&lt;/a&gt;. Dans cet article, je vais vous présenter Logstash, car il est gratuit, open source et multiplateforme (et parce qu’il faut bien faire un choix, après tout).&lt;/p&gt;

&lt;p&gt;Mais que ce soit Logstash ou un autre, les principes généraux restent les mêmes. À partir de fichiers de logs, l’outil va découper chaque évènement en plusieurs champs (date, sévérité, origine, message, …), stocker les évènements et proposer une interface permettant de les visualiser et de les requêter de manière efficace. Les outils les plus avancés sauront même vous envoyer un SMS, un courriel ou faire miauler votre chat en morse pour vous avertir d’un nouveau problème !&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Ship logs from any source, parse them, get the right timestamp, index them, and search them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;installation-et-utilisation-de-logstash&quot;&gt;Installation et utilisation de Logstash&lt;/h1&gt;

&lt;p&gt;L’installation de Logstash est simple comme hello world. Il suffit de récupérer l’archive de la dernière version sur &lt;a href=&quot;http://logstash.net/&quot;&gt;le site officiel&lt;/a&gt;, de l’extraire et voilà ! En regardant rapidement le contenu extrait, on remarque que l’application est faite en Ruby (avec un JRuby pour l’exécuter), et qu’elle embarque Elasticsearch et Kibana avec elle.&lt;/p&gt;

&lt;p&gt;Comme l’indique le &lt;a href=&quot;http://logstash.net/docs/1.4.2/tutorials/getting-started-with-logstash&quot;&gt;tutoriel&lt;/a&gt;, la manière la plus simple d’utiliser Logstash est la suivante :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;bin/logstash &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'input { stdin { } } output { stdout {} }'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Le flag &lt;code&gt;-e&lt;/code&gt; permet de spécifier la configuration dans la ligne de commande : les données en entrée seront prises dans &lt;code&gt;stdin&lt;/code&gt;, et le résultat sera envoyé vers &lt;code&gt;stdout&lt;/code&gt;. En tapant un message dans la console, Logstash réagit immédiatement :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;hello world
2013-11-21T01:22:14.405+0000 0.0.0.0 hello world&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;L’évènement généré se découpe en 3 champs : une date, une IP d’origine et un message.&lt;/p&gt;

&lt;p&gt;OK pour le hello world, maintenant il faut gérer un cas plus complexe, à savoir des logs serveur ayant une forme ressemblant à ceci :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;2015-03-16 13:37:42 ERROR prod.acme.com - p.t.d.r.MyClass - Une erreur est survenue, bla bla bla administrateur bla bla réessayer dans quelques minutes&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et puis tant qu’à faire, on voudrait bien stocker les évènements parsés pour pouvoir les requêter par la suite. C’est le moment de vous expliquer comment fonctionne la configuration de Logstash. Le traitement d’un évènement se divise en plusieurs phase : les &lt;em&gt;Inputs&lt;/em&gt;, les &lt;em&gt;Filters&lt;/em&gt;, les &lt;em&gt;Outputs&lt;/em&gt; et les &lt;em&gt;Codecs&lt;/em&gt;. La configuration simpliste de tout à l’heure (&lt;code&gt;input { stdin { } } output { stdout {} }&lt;/code&gt;) ne contenait qu’un input et un output. Au lieu de &lt;code&gt;stdout&lt;/code&gt;, nous voulons stocker (output) les données dans une base Elasticearch. Au lieu d’un simple message, nous voulons parser (filter) des logs serveur (input). La configuration ressemblerait alors à ceci :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;myServerLogs&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/var/log/jboss/server.log&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;grok&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;nx&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;%{TIME:logtime}%{SPACE}%{LOGLEVEL:rawlevel}%{SPACE}%{DATA:machine}%{SPACE}-%{SPACE}%{JAVACLASS:class}%{SPACE}-%{SPACE}%{GREEDYDATA:message}&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;yyyy-MM-dd HH:mm:ss&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;elasticsearch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;embedded&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;L’entrée est donc un fichier, et la sortie une base Elasticsearch embarquée. Le filtre utilise le &lt;a href=&quot;http://logstash.net/docs/1.4.2/filters/grok&quot;&gt;plugin grok&lt;/a&gt; qui permet de structurer un texte quelconque. On lui indique que chaque message est formatté selon les placeholders indiqués, et grok va découper chaque ligne de cette manière.&lt;/p&gt;

&lt;p&gt;La commande à lancer change en &lt;code&gt;bin/logstash -f myConf.conf web&lt;/code&gt;. On demande à Logstash d’utiliser un fichier de configuration nommé &lt;code&gt;myConf.conf&lt;/code&gt; et de lancer un serveur web pour visualiser le résultat. Une fois l’appli lancée, si on ajoute quelques lignes dans le fichier &lt;code&gt;server.log&lt;/code&gt;, Logstash devrait les prendre en compte. Pour le vérifier, connectons-nous à l’adresse &lt;code&gt;http://localhost:9292/&lt;/code&gt;. Miracle, une belle interface apparait. Il s’agit de &lt;a href=&quot;https://www.elastic.co/products/kibana&quot;&gt;Kibana&lt;/a&gt;, une interface de visualisation de données stockées dans une base Elasticseach (le monde est bien fait, hein). Kibana ne sait pas trop quoi faire, alors il nous propose plusieurs manières de configurer nous-même un dashboard répondant à nos attentes. Pour cela, nous allons cliquer sur le lien &lt;em&gt;“Sample Dashboard - I don’t have much data yet, please extract some basics for me”&lt;/em&gt;, qui nous redirige vers un dashboard ultra générique :&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/logstash/dashboard_kibana.png&quot;&gt;&lt;img src=&quot;/images/logstash/dashboard_kibana.png&quot; alt=&quot;kibana&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La partie &lt;em&gt;query&lt;/em&gt; permet de requêter les évènements, par exemple &lt;code&gt;rawlevel:ERROR&lt;/code&gt; pour filtrer les logs ERROR. La partie &lt;em&gt;filtering&lt;/em&gt; permet de filtrer les évènements affichés (semblable aux queries, mais appliqué tout le temps). De beaux graphiques permettent de visualiser rapidement les informations importantes (ici, la provenance des logs). Enfin, la partie basse liste les évènements. En cliquant sur une ligne, un tableau détaille chaque champ et sa valeur, ce qui confirme que nos logs ont bien été parsés :&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/logstash/detail_evenement.png&quot;&gt;&lt;img src=&quot;/images/logstash/detail_evenement.png&quot; alt=&quot;Détail d'un évènement&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Je vous laisse le soin de fouiner dans les possibilités offertes par Kibana, les dashboards seront très différent d’un projet à l’autre selon les besoins. Une &lt;a href=&quot;https://www.google.com/search?q=kibana+dashboard&amp;amp;tbm=isch&quot;&gt;recherche Google&lt;/a&gt; montre que l’on peut faire de très jolies choses (autant joindre l’utile à l’agréable).&lt;/p&gt;

&lt;h1 id=&quot;jaime-pas-parser-les-logs-&quot;&gt;J’aime pas parser les logs !&lt;/h1&gt;

&lt;p&gt;Tout ça est bien beau, mais en général les erreurs de nos chères applications Java sont accompagnées de leur fidèles &lt;em&gt;sidekicks&lt;/em&gt;, les stack traces. Et comme on ne peut plus se passer de proxies Spring, de filtres et de toutes ces choses qui s’ajoutent à la pile d’appels, celle-ci tend à devenir gigantesque. Par conséquent, un seul évènement de log va se répercuter sur plusieurs lignes, qu’il va falloir agréger pour qu’on s’y retrouve dans Kibana. Plusieurs solutions s’offrent à nous :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;utiliser le &lt;a href=&quot;http://www.logstash.net/docs/1.4.2/codecs/multiline&quot;&gt;codec multiline&lt;/a&gt; en complément de grok pour agréger les lignes&lt;/li&gt;
  &lt;li&gt;faire en sorte de ne plus avoir à parser les fichiers de logs en intervenant en amont&lt;/li&gt;
  &lt;li&gt;??&lt;/li&gt;
  &lt;li&gt;la réponse d)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Je vous avoue que j’ai testé la première solution sans vraiment y arriver. Du coup, je me suis dit que la seconde solution serait intéressante si on avait la possibilité de se brancher directement sur SLF4J (comment ça, vous utilisez Log4J directement ?) pour envoyer les évènements à Elasticsearch sans passer par la case parsing.&lt;/p&gt;

&lt;p&gt;C’est le moment d’appeler &lt;a href=&quot;https://github.com/logstash/logstash-logback-encoder&quot;&gt;logstash-logback-encoder&lt;/a&gt; à la rescousse. Cet appender pour Logback permet d’encoder les messages loggués en JSON, puis d’envoyer le tout via une socket TCP à Logstash. Comme vous pouvez l’imaginer, le contenu JSON est directement structuré comme il faut, plus besoin de parser avec des regex, d’agréger des lignes, etc. Après avoir récupéré la bonne dépendance, la configuration du &lt;code&gt;logback.xml&lt;/code&gt; est très simple :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;appender&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stash&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;net.logstash.logback.appender.LogstashTcpSocketAppender&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  	&lt;span class=&quot;nt&quot;&gt;&amp;lt;filter&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ch.qos.logback.classic.filter.ThresholdFilter&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  		&lt;span class=&quot;nt&quot;&gt;&amp;lt;level&amp;gt;&lt;/span&gt;WARN&lt;span class=&quot;nt&quot;&gt;&amp;lt;/level&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;/filter&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;host&amp;gt;&lt;/span&gt;127.0.0.1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/host&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;port&amp;gt;&lt;/span&gt;4560&lt;span class=&quot;nt&quot;&gt;&amp;lt;/port&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;encoder&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;net.logstash.logback.encoder.LogstashEncoder&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/appender&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;root&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;level=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;debug&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  	&lt;span class=&quot;nt&quot;&gt;&amp;lt;appender-ref&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;STDOUT&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;appender-ref&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stash&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/root&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Nous avons donc défini un appender basé sur une socket TCP, qui envoie les évènements sur &lt;code&gt;127.0.0.1:4560&lt;/code&gt;. L’&lt;code&gt;encoder&lt;/code&gt; permet de sérialiser les données au format JSON. Notez que le niveau de logs par défaut est &lt;code&gt;DEBUG&lt;/code&gt;, mais qu’il est possible de le restreindre par exemple à &lt;code&gt;WARN&lt;/code&gt; en utilisant un &lt;code&gt;ThresholdFilter&lt;/code&gt;, puisque nous ne sommes intéressés que par les WARN/ERROR. L’appender &lt;code&gt;STDOUT&lt;/code&gt; continuera de logguer les niveaux &amp;gt;= DEBUG.&lt;/p&gt;

&lt;p&gt;Côté Logstash, pour pouvoir recevoir correctement les évènements, on configure une entrée TCP, encodée en JSON, sur le port adéquat :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;tcp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4560&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;codec&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Après redémarrage de Logstash, les évènements sont loggués comme prévu. Il est à noter que l’appender est asynchrone (&lt;a href=&quot;https://lmax-exchange.github.io/disruptor/&quot;&gt;LMAX Disruptor RingBuffer&lt;/a&gt; inside), il ne bloquera jamais le thread qui génère les logs. En cas de problème réseau, l’appender peut se reconnecter à Logstash. Si le buffer est plein, certains évènements peuvent toutefois être perdus.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;J’espère que cet article vous a donné envie de tester Logstash sur votre projet. Comme vous avez pu le remarquer, la configuration est relativement simple lorsqu’il s’agit d’indexer les erreurs et stacktraces. Je n’ai volontairement pas compliqué les choses, mais sachez qu’il est possible de faire des choses relativements complexes aussi bien dans l’indexation des logs que dans les dashboards côté Kibana (largement de quoi s’adapter à votre configuration et vos besoins). De plus, Logstash est capable de parser tout et n’importe quoi, il propose par défaut une série de &lt;a href=&quot;https://github.com/elastic/logstash/tree/v1.4.2/patterns&quot;&gt;patterns&lt;/a&gt; permettant de parser des dates, des logs Apache, Syslog, Nagios, MongoDB etc.&lt;/p&gt;

&lt;h1 id=&quot;pour-aller-plus-loin&quot;&gt;Pour aller plus loin&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://logstash.net/docs/1.4.2/tutorials/getting-started-with-logstash&quot;&gt;Getting Started With Logstash&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://logstash.net/docs/1.4.2&quot;&gt;Liste et documentation des plugins Logstash&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://grokdebug.herokuapp.com/&quot;&gt;Un testeur de regex grok&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Il parait que les allemands disent “La prévention est la mère de la caisse à porcelaine” &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Rien à voir avec &lt;a href=&quot;https://bitbucket.org/bjansen/stackhunter&quot;&gt;mon projet perso&lt;/a&gt;, qui fait la même chose mais qui n’est pas aussi abouti &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Tue, 17 Mar 2015 11:55:00 +0000</pubDate>
        <link>http://bjansen.github.io/java/2015/03/17/monitorer-ses-logs-avec-logstash.html</link>
        <guid isPermaLink="true">http://bjansen.github.io/java/2015/03/17/monitorer-ses-logs-avec-logstash.html</guid>
        
        
        <category>Java</category>
        
      </item>
    
      <item>
        <title>Un switch annoté en Java</title>
        <description>&lt;p&gt;Un billet sur le blog de &lt;a href=&quot;http://agilitateur.azeau.com/post/2010/07/01/Un-switch-%C3%A9volutif-en-C&quot;&gt;l’Agilitateur&lt;/a&gt; répondant à un billet sur le blog &lt;a href=&quot;http://blog.excilys.com/2010/06/25/de-switcher-nest-pas-jouer&quot;&gt;d’Excilys&lt;/a&gt; concernant les switchs en java me donne envie de parler un peu de codage en Java. Cet article est une variante du code C# proposé par Oaz en Java.&lt;/p&gt;

&lt;p&gt;Je vais vous épargner toute la discussion sur le switch c'est bien / c'est nul, il y a déjà les &lt;a href=&quot;http://blog.excilys.com/2010/06/25/de-switcher-nest-pas-jouer/comment-page-1/&quot;&gt;commentaires enflammés&lt;/a&gt; sur le blog d'Excilys pour ça.&lt;/p&gt;
&lt;p&gt;
Passons directement au code : de la même manière qu'Oaz l'a fait, je dispose d'une interface Function qui définit le contrat que vont suivre les différents cas du switch. Pour que l'on sache que les différentes sous-classes sont bien des cases, on les annote avec un @Case créé pour l'occasion :


&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@Retention&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RetentionPolicy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;RUNTIME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@Target&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ElementType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;TYPE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;@interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;


On demande au compilateur de garder cette annotation au runtime (pour faire du scanning dynamique), et on précise qu'elle peut s'appliquer à un type (classe ou interface).
&lt;/p&gt;

&lt;p&gt;On peut alors utiliser l'annotation de cette façon :


&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Function&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;doYourJob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Case&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BarFunction&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Function&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;doYourJob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;On va manger... des chips ! T'entends ? DES CHIPS !!&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Case&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FooFunction&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Function&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;doYourJob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hi, my name is foo!&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Case&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;othercase&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OtherCase&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;doYourJob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;I'm not gonna be called :-(&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;/p&gt;

&lt;p&gt;Reste maintenant le plus dur à faire : scanner un package à la recherche de classes annotées @Case et qui héritent de notre classe de base (ici Function, mais on va essayer de rester génériques).


&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Switch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;packageName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cases&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parentType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Switch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;packageName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parentType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;packageName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;packageName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;parentType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parentType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;scanCases&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;scanCases&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cases&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clazz&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getClasses&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;packageName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clazz&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isAnnotationPresent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Case&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clazz&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parentType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;nc&quot;&gt;Case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;theCase&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Case&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clazz&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getAnnotation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Case&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;cases&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;theCase&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clazz&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;newInstance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;printStackTrace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Scanned &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cases&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; cases.&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clazz&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clazz&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSuperclass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;intf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clazz&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getInterfaces&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;intf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;caseName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IllegalArgumentException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cases&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;containsKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;caseName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cases&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;caseName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IllegalArgumentException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The case &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;caseName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; does not exist&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getClasses&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pckgname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;


J'ai fait appel au &lt;a href=&quot;http://java.sun.com/j2se/1.5.0/docs/guide/language/generics.html&quot;&gt;generic type&lt;/a&gt; introduit en Java5, cela permet d'avoir un switch acceptant n'importe quelle classe de base qu'implémentent les différents cas. J'ai été obligé de repasser cette information dans le constructeur car en Java il n'est pas possible de faire un T.class dans la phase de scan des classes (pour savoir si la classe scannée étend ou implémente T). À la place, on a donc un Class&lt;T&gt; en second paramètre.&amp;lt;/p&amp;gt;
&lt;p&gt;La méthode scanCases va parcourir toutes les classes d'un package (grâce à la méthode getClasses() honteusement &lt;a href=&quot;http://forums.sun.com/thread.jspa?messageID=3946549#3946549&quot;&gt;trouvée sur le forum de &lt;strike&gt;Sun&lt;/strike&gt; Oracle&lt;/a&gt;). Pour chaque classe, on regarde si elle est annotée @Case et si elle étend ou implémente T. Si c'est le cas, on en ajoute une instance dans une Map avec le nom du cas comme clé.&lt;/p&gt;
&lt;p&gt;Dernière étape : la méthode on() qui renvoie l'instance associée à un cas ou jette une exception s'il n'existe pas.&lt;/p&gt;
&lt;p&gt;La glue entre tout ça, c'est un main assez simple : 


&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Main&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Switch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;swi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Switch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;com.jansen.annotatedswitch.demo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        
        &lt;span class=&quot;n&quot;&gt;swi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;doYourJob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;swi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;doYourJob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;swi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;othercase&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;doYourJob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;


Notez le dernier appel qui va planter : j'ai bien une classe annotée @Case(name=&quot;othercase&quot;) mais elle n'implémente pas Function (une ruse de sioux pour égarer le lecteur peu attentif ;-) ).&lt;/p&gt;

&lt;p&gt;Conclusion : c'est aussi possible en Java, avec un peu de gymnastique intellectuelle pour arriver à faire ce qu'on souhaite avec les generics :P&lt;/p&gt;
&lt;/T&gt;&lt;/p&gt;
</description>
        <pubDate>Fri, 02 Jul 2010 12:05:00 +0000</pubDate>
        <link>http://bjansen.github.io/java/2010/07/02/un-switch-annote-en-java.html</link>
        <guid isPermaLink="true">http://bjansen.github.io/java/2010/07/02/un-switch-annote-en-java.html</guid>
        
        
        <category>Java</category>
        
      </item>
    
      <item>
        <title>Le logger de la mort</title>
        <description>&lt;p&gt;Parfois, au cours d'une mission chez un client, on tombe sur du code pas testé, et pas toujours prévu pour être testé non plus. Je vais présenter un cas sur lequel je suis tombé récemment, issu d'un assez gros projet Java EE en cours de développement.&lt;/p&gt;

&lt;p&gt;Une classe de services X effectue une certain nombre de traitements. En cas d’erreur, au lieu de jeter une exception métier, un code d’erreur est loggué. Impossible d’utiliser un &lt;code&gt;@Test(expected=...)&lt;/code&gt;, donc. Fort heureusement, le champ logger dans X est une interface, potentiellement mockable :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Ilogger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;logError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// blabla&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Dans tous les cas où nous sommes intéressés, la méthode logError est appelée avec un paramètre Key représentant le code d’erreur. Malheureusement, le logger n’est pas injecté dans un setter, et il est privé. On aimerait tout de même vérifier que l’erreur est bien logguée sous certaines conditions. Solution, un peu de réflexion pour forcer le champ logger à une instance que l’on contrôle :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MockLogger&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ILogger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loggedKeys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MockLogger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;loggedKeys&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;();&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;logError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;loggedKeys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;wasLogged&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loggedKeys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;resetLogCalls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;loggedKeys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;La méthode &lt;code&gt;resetLogCalls&lt;/code&gt; permet d’utiliser la même instance du logger sur plusieurs tests, il faut l’appeler dans une méthode annotée &lt;code&gt;@After&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Il faut maintenant injecter notre propre logger dans le service testé :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;nc&quot;&gt;Field&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serviceUnderTest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getDeclaredField&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;logger&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setAccessible&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;serviceUnderTest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;myLoggerInstance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setAccessible&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et voila, nous pouvons maintenant faire des &lt;code&gt;assertTrue&lt;/code&gt; utilisant la méthode &lt;code&gt;wasLogged&lt;/code&gt; et la Key nous intéressant ! Notez l’appel à &lt;code&gt;Field#setAccessible(boolean)&lt;/code&gt; pour contourner le caractère private du champ &lt;code&gt;logger&lt;/code&gt; (que l’on restaure par la suite).&lt;/p&gt;

&lt;p&gt;Note : si le logger n’avait pas utilisé d’interface, nous aurions pu nous en sortir en créant une classe fille redéfinissant toutes les méthodes sans appels à &lt;code&gt;super()&lt;/code&gt;.&lt;/p&gt;
</description>
        <pubDate>Thu, 04 Feb 2010 16:24:00 +0000</pubDate>
        <link>http://bjansen.github.io/java/2010/02/04/le-logger-de-la-mort.html</link>
        <guid isPermaLink="true">http://bjansen.github.io/java/2010/02/04/le-logger-de-la-mort.html</guid>
        
        
        <category>Java</category>
        
      </item>
    
      <item>
        <title>Contrôler la bande passante de sa machine linux</title>
        <description>&lt;p&gt;Le haut débit c'est bien, mais des fois on a quand même un côté nostalgique du 56k, soit parce qu'on a envie d'embêter les visiteurs de son serveur, soit parce que pour débugger une application (par exemple) on a envie que les données arrivent lentement. Sous linux, il existe la commande &lt;code&gt;tc&lt;/code&gt;, pour &lt;i&gt;traffic control&lt;/i&gt;, qui permet de contrôler ses connexions réseaux, notamment leur débit...&lt;/p&gt;
&lt;p&gt;J'ai donc trouvé un &lt;a href=&quot;http://www.topwebhosts.org/tools/traffic-control.php&quot;&gt;script tout fait&lt;/a&gt; permettant de limiter la bande passante d'une interface réseau, en upload ou en download.
&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#  tc uses the following units when passed as a parameter.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#  kbps: Kilobytes per second &lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#  mbps: Megabytes per second&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#  kbit: Kilobits per second&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#  mbit: Megabits per second&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#  bps: Bytes per second &lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#       Amounts of data can be specified in:&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#       kb or k: Kilobytes&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#       mb or m: Megabytes&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#       mbit: Megabits&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#       kbit: Kilobits&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#  To get the byte figure from bits, divide the number by 8 bit&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Name of the traffic control command.&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;TC&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/sbin/tc

&lt;span class=&quot;c&quot;&gt;# The network interface we're planning on limiting bandwidth.&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;eth0             &lt;span class=&quot;c&quot;&gt;# Interface&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Download limit (in mega bits)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;DNLD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1mbit          &lt;span class=&quot;c&quot;&gt;# DOWNLOAD Limit&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Upload limit (in mega bits)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;UPLD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1mbit          &lt;span class=&quot;c&quot;&gt;# UPLOAD Limit&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# IP address of the machine we are controlling&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;IP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;216.3.128.12     &lt;span class=&quot;c&quot;&gt;# Host IP&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Filter options for limiting the intended interface.&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;U32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$TC&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; filter add dev &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$IF&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; protocol ip parent 1:0 prio 1 u32&quot;&lt;/span&gt;

start&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# We'll use Hierarchical Token Bucket (HTB) to shape bandwidth.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# For detailed configuration options, please consult Linux man&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# page.&lt;/span&gt;

    &lt;span class=&quot;nv&quot;&gt;$TC&lt;/span&gt; qdisc add dev &lt;span class=&quot;nv&quot;&gt;$IF&lt;/span&gt; root handle 1: htb default 30
    &lt;span class=&quot;nv&quot;&gt;$TC&lt;/span&gt; class add dev &lt;span class=&quot;nv&quot;&gt;$IF&lt;/span&gt; parent 1: classid 1:1 htb rate &lt;span class=&quot;nv&quot;&gt;$DNLD&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$TC&lt;/span&gt; class add dev &lt;span class=&quot;nv&quot;&gt;$IF&lt;/span&gt; parent 1: classid 1:2 htb rate &lt;span class=&quot;nv&quot;&gt;$UPLD&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$U32&lt;/span&gt; match ip dst &lt;span class=&quot;nv&quot;&gt;$IP&lt;/span&gt;/32 flowid 1:1
    &lt;span class=&quot;nv&quot;&gt;$U32&lt;/span&gt; match ip src &lt;span class=&quot;nv&quot;&gt;$IP&lt;/span&gt;/32 flowid 1:2

&lt;span class=&quot;c&quot;&gt;# The first line creates the root qdisc, and the next two lines&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# create two child qdisc that are to be used to shape download &lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# and upload bandwidth.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# The 4th and 5th line creates the filter to match the interface.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# The 'dst' IP address is used to limit download speed, and the &lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 'src' IP address is used to limit upload speed.&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

stop&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Stop the bandwidth shaping.&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$TC&lt;/span&gt; qdisc del dev &lt;span class=&quot;nv&quot;&gt;$IF&lt;/span&gt; root

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

restart&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Self-explanatory.&lt;/span&gt;
    stop
    &lt;span class=&quot;nb&quot;&gt;sleep &lt;/span&gt;1
    start

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

show&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Display status of traffic control status.&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$TC&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; qdisc &lt;span class=&quot;nb&quot;&gt;ls &lt;/span&gt;dev &lt;span class=&quot;nv&quot;&gt;$IF&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in

  &lt;/span&gt;start&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Starting bandwidth shaping: &quot;&lt;/span&gt;
    start
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;done&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;

  stop&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Stopping bandwidth shaping: &quot;&lt;/span&gt;
    stop
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;done&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;

  restart&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Restarting bandwidth shaping: &quot;&lt;/span&gt;
    restart
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;done&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;

  show&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Bandwidth shaping status for &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$IF&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&quot;&lt;/span&gt;
    show
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Usage: tc.bash {start|stop|restart|show}&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;esac&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;0&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
</description>
        <pubDate>Tue, 03 Nov 2009 14:21:00 +0000</pubDate>
        <link>http://bjansen.github.io/scripts/2009/11/03/controler-la-bande-passante-de-sa-machine-linux.html</link>
        <guid isPermaLink="true">http://bjansen.github.io/scripts/2009/11/03/controler-la-bande-passante-de-sa-machine-linux.html</guid>
        
        
        <category>scripts</category>
        
      </item>
    
      <item>
        <title>Pourquoi Netbeans c'est bien</title>
        <description>&lt;p&gt;C'est un fait avéré, Netbeans c'est mieux qu'Eclipse. &lt;small&gt;(comment ça, un troll tout poilu ?)&lt;/small&gt;. Pas plus tard qu'il y a une minute, j'en ai refait l'heureuse expérience. Agacé de voir une série de getters/setters dans ma classe, qui sont triviaux à comprendre mais qui prennent de la place, j'ai voulu utiliser l'&lt;a href=&quot;http://wiki.netbeans.org/FaqCustomCodeFolds&quot;&gt;editor folding&lt;/a&gt;, propre à Netbeans (je crois, du moins).

&lt;!-- more --&gt;

Ça consiste juste en deux commentaires autour du code que l'on veut &lt;i&gt;folder&lt;/i&gt; (euh... comment traduit-on ça en français, déjà...) :


&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// &amp;lt;editor-fold defaultstate=&quot;collapsed&quot; desc=&quot;getters and setters&quot;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getFoo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setFoo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// &amp;lt;/editor-fold&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;


Maintenant vous aurez un joli '-' sur la gauche qui permettra de cacher tout ça et qui affichera la description voulue (ici, &quot;getters and setters&quot;).&lt;/p&gt;

&lt;p&gt;Amusons-nous encore un peu, en transformant ça en &lt;a href=&quot;http://wiki.netbeans.org/Java_EditorUsersGuide#section-Java_EditorUsersGuide-HowToUseLiveTemplates&quot;&gt;code template&lt;/a&gt; :&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/netbeans/code-templates.png&quot; alt=&quot;Code templates dans Netbeans&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Et c'est parti, sélectionnez un bloc de texte que vous ne voulez plus voir, puis cliquer sur l'ampoule dans la gutter et choisissez &quot;Surround with an editor fold tag&quot;. Magique, non ?&lt;/p&gt;
</description>
        <pubDate>Wed, 25 Mar 2009 15:16:00 +0000</pubDate>
        <link>http://bjansen.github.io/netbeans/2009/03/25/pourquoi-netbeans-c-est-bien.html</link>
        <guid isPermaLink="true">http://bjansen.github.io/netbeans/2009/03/25/pourquoi-netbeans-c-est-bien.html</guid>
        
        
        <category>Netbeans</category>
        
      </item>
    
      <item>
        <title>Récupérer de la musique depuis Deezer</title>
        <description>&lt;p&gt;Même si je ne suis pas trop fan de &lt;a href=&quot;http://www.deezer.com&quot;&gt;Deezer&lt;/a&gt; pour diverses raisons (interface Flash trop lourde, beaucoup trop de pub, ...), je suis quand même bien content de pouvoir y rechercher une musique que j'ai en tête et que je peux récupérer pour la réécouter par la suite...&lt;/p&gt;

&lt;p&gt;En effet, il est de notoriété publique que ce système met en cache la chason en cours d'écoute et la stocke dans un fichier flv nommé /tmp/Flash* sous linux.&lt;/p&gt;
&lt;p&gt;À partir de là, il est facile de créer un script shell qui va se charger de récupérer ce ficher, puis d'en extraire la musique en MP3 et de la sauvegarder dans notre dossier de musique favori. Top du top, le script que je vous propose par la suite permet également d'éditer les tags ID3 et de renommer le fichier en conséquence, puis ajoute le morceau à la playlist Amarok en cours :D&lt;/p&gt;
&lt;p&gt;Voici la bête :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;DEST&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/home/user/Musique

&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$DEST&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ls&lt;/span&gt; /tmp/Flash&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;cut&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f3&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;file &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$files&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;do
        &lt;/span&gt;ffmpeg &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/tmp/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-acodec&lt;/span&gt; copy &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; mp3 &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DEST&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.mp3&quot;&lt;/span&gt;
        id3ren &lt;span class=&quot;nt&quot;&gt;-quick&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-notrack&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-template&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%a - %s.mp3&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DEST&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.mp3&quot;&lt;/span&gt;
        dcop amarok playlist addMedia &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DEST&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
</description>
        <pubDate>Thu, 19 Mar 2009 14:15:00 +0000</pubDate>
        <link>http://bjansen.github.io/scripts/2009/03/19/recuperer-de-la-musique-depuis-deezer.html</link>
        <guid isPermaLink="true">http://bjansen.github.io/scripts/2009/03/19/recuperer-de-la-musique-depuis-deezer.html</guid>
        
        
        <category>scripts</category>
        
      </item>
    
      <item>
        <title>Security holes in symfony apps</title>
        <description>&lt;p&gt;symfony 1.2 was just released, and came along with a brand new advent calendar. &lt;a href=&quot;http://www.symfony-project.org/jobeet/1_2/en/01&quot;&gt;The first day&lt;/a&gt; mentions two ways to set up your web server to host a symfony project. &lt;a href=&quot;http://www.symfony-project.org/jobeet/1_2/en/01#Web Server Setup: The ugly Way&quot;&gt;The ugly way&lt;/a&gt; seems to be very... ugly and crazy, putting your app directly in your webserver's root folder allows anyone to access all the files. But this security hole can be easily found in 'real' apps, as well as a few others...&lt;/p&gt;
&lt;p&gt;
This article will try to enumerate some of the mistakes that are often done when a symfony application is put in production environment.
&lt;/p&gt;

&lt;h2&gt;Development environment is still present&lt;/h2&gt;
&lt;p&gt;These files, ending with _dev.php, are very useful when you are coding: they provide a lot of information on the configuration, the SQL queries that were executed, your PHP version etc. But on the other hand, they should never be accessible to your visitors (I mean, bad ones, you know, those who type in the darkness). They can show your kernel version, the symfony plugins which are installed, the escaping method you are using (or &lt;i&gt;not&lt;/i&gt; using...), etc., which can lead to other vulnerabilities.&lt;p&gt;

&lt;h2&gt;The project root is the webserver's root folder&lt;/h2&gt;
&lt;p&gt;This can also give a lot of information to an attacker, especially your database connection settings. Just search for 'web/frontend.php' in your favorite search engine, and you will have a bunch of apps which are potentially in this case. Then you just have to browse config/databases.yml and you get what you want. Just consider that if the application is unsecure, the webserver which is running it is also unsecure, and you can have a remote access to MySQL on port 3306, which will show you a lot of interesting things...&lt;/p&gt;

&lt;h2&gt;The backend is not protected&lt;/h2&gt;
&lt;p&gt;
Harder to find, but not impossible. Even if this page is not indexed by search engines, it's quite easy to see if a web app is powered by symfony, and the majority of these web apps have a backend which is called 'backend' (if not, just try 'admin' :)). So it's very important to restrict access to this backend, as it can contain sensitive information (user accounts, etc.). Imagine that you didn't make a recent backup of your database, and that someone deletes all its content...
&lt;/p&gt;

&lt;h2&gt;How to fix it?&lt;/h2&gt;
&lt;p&gt;
It's not difficult to avoid these problems. In fact, the solutions are very well explained in the official symfony book, but nonetheless they are often not applied.&lt;/p&gt;
&lt;ul&gt;
 &lt;li&gt;Check that *_dev.php files are not uploaded (see the rsync_excludes.txt file for example)&lt;/li&gt;
 &lt;li&gt;If you can, put the whole project (except the web/ folder) outside the web document root&lt;/li&gt;
 &lt;li&gt;Protect your backend with sfGuard, or at least a htaccess/htpasswd file :) (and do not user admin/admin or something like this)&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;/p&gt;
</description>
        <pubDate>Mon, 01 Dec 2008 21:52:00 +0000</pubDate>
        <link>http://bjansen.github.io/symfony/2008/12/01/security-holes-in-symfony-apps.html</link>
        <guid isPermaLink="true">http://bjansen.github.io/symfony/2008/12/01/security-holes-in-symfony-apps.html</guid>
        
        
        <category>symfony</category>
        
      </item>
    
  </channel>
</rss>
