<?xml version="1.0"?>
<rss version="2.0">
    <channel>
        <title>RPG Next Gen</title>
        <link>http://example.com</link>
        <description></description>
        <language>en</language>
        <pubDate>Fri, 16 Jan 2026 00:00:00 +0100</pubDate>
        <lastBuildDate>Fri, 16 Jan 2026 00:00:00 +0100</lastBuildDate>
        <category>Java</category>
        <category>ILEDocs</category>
        <category>BlueDroplet</category>
        <category>CI</category>
        <category>XMLSERVICE</category>
        <category>RPG</category>
        <category>STOMP</category>
        <category>XML</category>
        <category>SQL</category>
        <category>CLOB</category>
        <category>Design</category>
        <category>CodeNotes</category>
        <category>RDi</category>
        <category>arraylist</category>
        <category>libxlsxwriter</category>
        <category>node.js</category>
        <category>iPKG</category>
        <category>Kong</category>
        <category>ipkg</category>
        <category>IBMi</category>
        <category>ILEastic</category>
        <category>IBM</category>
        <category>ILEvator</category>
        <item>
            <guid isPermalink="true">http://example.com/2026/01/16/2026-01-16-batch-ws/</guid>
            <title>Batch - Consuming Web Services</title>
            <link>http://example.com/2026/01/16/2026-01-16-batch-ws/</link>
            <category>Java</category>
            <pubDate>Fri, 16 Jan 2026 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;h1 id=&#34;Batch-Consuming-Web-Services&#34;&gt;&lt;a href=&#34;#Batch-Consuming-Web-Services&#34; class=&#34;headerlink&#34; title=&#34;Batch - Consuming Web Services&#34;&gt;&lt;/a&gt;Batch - Consuming Web Services&lt;/h1&gt;&lt;p&gt;Most of the time the input for a batch workflow comes from databases or files. These are
already very easily accessable through various libraries.&lt;/p&gt;
&lt;p&gt;In the last years web services have gained much popularity and is one if not &lt;em&gt;the&lt;/em&gt; most common
way to exchange data. Most of the time the data is presented in JSON.&lt;/p&gt;
&lt;h2 id=&#34;Object-by-Object&#34;&gt;&lt;a href=&#34;#Object-by-Object&#34; class=&#34;headerlink&#34; title=&#34;Object by Object&#34;&gt;&lt;/a&gt;Object by Object&lt;/h2&gt;&lt;p&gt;For very large sets of data it is very inconvenient to load the whole data set into memory before
processing. I already made a post about how to process a JSON object by object by using the library
Jackson, see &lt;a href=&#34;https://blog.rpgnextgen.com/2025/01/13/2025-01-13-streaming-client/&#34;&gt;Streaming data - the client side&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;Authentication&#34;&gt;&lt;a href=&#34;#Authentication&#34; class=&#34;headerlink&#34; title=&#34;Authentication&#34;&gt;&lt;/a&gt;Authentication&lt;/h2&gt;&lt;p&gt;Many web services are secured by something like OAuth 2.0 and OpenID Connect. OpenID Connect
explicitly has a &lt;em&gt;grant type&lt;/em&gt; for batch programs or M2M (machine to machine) communications:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Client Credentials&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So an access token can be obtained from your OIDC provider just by passing a client id and a
client secret. To make this as easy as it sounds I created a new project which contains an
&lt;code&gt;OidcTokenProvider&lt;/code&gt;. It just needs the configuration passed in the
&lt;a href=&#34;https://download.eclipse.org/microprofile/microprofile-config-3.0/microprofile-config-spec-3.0.html&#34;&gt;MicroProfile Config&lt;/a&gt;
way like using a &lt;code&gt;microprofile-config.properties&lt;/code&gt; file or using environment variables.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;batch.ws.oidc.client-id&lt;/li&gt;
&lt;li&gt;batch.ws.oidc.client-secret&lt;/li&gt;
&lt;li&gt;batch.ws.oidc.scope (default: openid)&lt;/li&gt;
&lt;li&gt;batch.ws.oidc.url (token URL)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then the &lt;code&gt;OidcTokenProvider&lt;/code&gt; can be injected and an access token obtained.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@Inject&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; OidcTokenProvider tokenProvider;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;type&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;accessToken&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; tokenProvider.get();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;Multiple-Token-Provider&#34;&gt;&lt;a href=&#34;#Multiple-Token-Provider&#34; class=&#34;headerlink&#34; title=&#34;Multiple Token Provider&#34;&gt;&lt;/a&gt;Multiple Token Provider&lt;/h2&gt;&lt;p&gt;The library provides a prepackaged token provider which can be used out-of-the-box (with the
corresponding configuration). But sometimes we need to use multiple web services and each may
have their own OIDC provider or client id. That means we need two instances of &lt;code&gt;OidcTokenProvider&lt;/code&gt;
with two different configurations.&lt;/p&gt;
&lt;p&gt;This can be achieved with some lines of code. A new class needs to be created which extends
&lt;code&gt;AbstractOidcTokenProvider&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@ApplicationScoped&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;PackagingOidcTokenProvider&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;AbstractOidcTokenProvider&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;meta&#34;&gt;@Inject&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title function_&#34;&gt;PackagingOidcTokenProvider&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;(&lt;span class=&#34;meta&#34;&gt;@ConfigProperties(prefix = &amp;quot;packaging&amp;quot;)&lt;/span&gt; OidcTokenProviderConfig config)&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		&lt;span class=&#34;built_in&#34;&gt;super&lt;/span&gt;(config);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;By prefixing the config properties (&lt;code&gt;prefix = &amp;quot;packaging&amp;quot;&lt;/code&gt;) two seperate configuration instances
can exist side by side. And instead of &lt;code&gt;batch.ws.oidc.url&lt;/code&gt; the configuration &lt;code&gt;packaging.oidc.url&lt;/code&gt;
is used for this instance. And both token provider instances can be injected into the same bean.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@Inject&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; OidcTokenProvider tokenProvider;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@Inject&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; PackagingOidcTokenProvider packagingTokenProvider;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;Source-Code&#34;&gt;&lt;a href=&#34;#Source-Code&#34; class=&#34;headerlink&#34; title=&#34;Source Code&#34;&gt;&lt;/a&gt;Source Code&lt;/h2&gt;&lt;p&gt;All those classes have been grouped together into the library &lt;code&gt;batch-ws&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight xml&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;dependency&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;groupId&lt;/span&gt;&amp;gt;&lt;/span&gt;com.rpgnextgen&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;groupId&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;artifactId&lt;/span&gt;&amp;gt;&lt;/span&gt;batch-ws&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;artifactId&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;version&lt;/span&gt;&amp;gt;&lt;/span&gt;[1.0.0,2.0.0)&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;version&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;dependency&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The source code is available at &lt;a href=&#34;https://bitbucket.org/m1hael/batch-ws&#34;&gt;Bitbucket&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;Wrap-Up&#34;&gt;&lt;a href=&#34;#Wrap-Up&#34; class=&#34;headerlink&#34; title=&#34;Wrap Up&#34;&gt;&lt;/a&gt;Wrap Up&lt;/h2&gt;&lt;p&gt;Accessing web services in a batch prorgram can be very easy and even memory friendly. No need to
dump the content to a local file and bother with CCSIDs and clean up strategies.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2026/01/04/2026-01-04-batch-core-el%20copy/</guid>
            <title>Batch Core - Dynamic Business Rules</title>
            <link>http://example.com/2026/01/04/2026-01-04-batch-core-el%20copy/</link>
            <category>Java</category>
            <pubDate>Sun, 04 Jan 2026 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;h1 id=&#34;Batch-Core-Dynamic-Business-Rules&#34;&gt;&lt;a href=&#34;#Batch-Core-Dynamic-Business-Rules&#34; class=&#34;headerlink&#34; title=&#34;Batch Core - Dynamic Business Rules&#34;&gt;&lt;/a&gt;Batch Core - Dynamic Business Rules&lt;/h1&gt;&lt;p&gt;In this Java batch series I hardcoded the business rules in the program. This may be ok for some
cases but in our fast moving world having the ability to change business rules without changing
the program is a big plus.&lt;/p&gt;
&lt;p&gt;And it is not only about having the end user change the business rules. I wouldn’t go that far in
my first step. Having business rules extracted to a text file or a database is already a big plus
because you can instantly change a business rule without too much fuzz.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Does the new or changed business rule needs some kind of testing?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yes, definitely! But we don’t need to compile and test the whole program, just the business rule.&lt;/p&gt;
&lt;p&gt;So how to implement that?&lt;/p&gt;
&lt;h2 id=&#34;Implementation&#34;&gt;&lt;a href=&#34;#Implementation&#34; class=&#34;headerlink&#34; title=&#34;Implementation&#34;&gt;&lt;/a&gt;Implementation&lt;/h2&gt;&lt;p&gt;Having such a mature programming language as Java has its advantages. There probably is already a
solution to your problem as most developers don’t face brand new problems. Or there is a solution
which can be modified so it fits your problem.&lt;/p&gt;
&lt;p&gt;And that is also the case here. Dynamic business rules can be easily implemented using the
&lt;a href=&#34;https://jakarta.ee/specifications/expression-language/6.0/jakarta-expression-language-spec-6.0&#34;&gt;Jakarta Expression Language&lt;/a&gt;.
Most have heard of Jakarta EL (Expression Language) in conjunction with Java Server Faces and Java
Server Pages. But it can also be used stand alone, outside of Jakarta EE servers.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/eclipse-ee4j/expressly&#34;&gt;Eclipse Expressly&lt;/a&gt; is the reference implementation
of the Jakarta EL specs. And if we are taking a look at the dependencies of Expressly we
astonishingly see only a single dependency – the EL API. That means we can use Expressly in many
scenarios and environments.&lt;/p&gt;
&lt;h2 id=&#34;User-Defined-Context&#34;&gt;&lt;a href=&#34;#User-Defined-Context&#34; class=&#34;headerlink&#34; title=&#34;User Defined Context&#34;&gt;&lt;/a&gt;User Defined Context&lt;/h2&gt;&lt;p&gt;Mostly there are two ways to use Jakarta EL.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;evaluate an expression and use the result of the evaluation for further processing&lt;/li&gt;
&lt;li&gt;execute an expression and do every processing inside the expression&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If we evaluate an expression and work with the result that would mean that we need to hard code
what to do with the result. That would remove some of the dynamics we get from using an
expression language.&lt;/p&gt;
&lt;p&gt;The solution is … providing your own context (or rather your own &lt;code&gt;ELResolver&lt;/code&gt; class). That means
we can add our own objects to the expression context so that our own objects are available in the
expression and thus can be queried and changed (despite ChatGPT saying that this is not possible,
so much for our glorious AIs &amp;#128516;).&lt;/p&gt;
&lt;p&gt;ChatGPTs answer:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;❌ Not possible in EL&lt;/p&gt;
&lt;p&gt;You cannot do this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;loop + modify entity field&lt;/li&gt;
&lt;li&gt;assign values in EL&lt;/li&gt;
&lt;li&gt;create mutable counters in EL&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;because EL has no assignment operators.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But I can do this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;entity.packagingWorkplace = entity.line == 5 ? 5 : entity.packagingWorkplace&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;… which assigns a value to a field by using an operator. AI is so ridiculous.&lt;/p&gt;
&lt;h2 id=&#34;Business-Rules-Expressions&#34;&gt;&lt;a href=&#34;#Business-Rules-Expressions&#34; class=&#34;headerlink&#34; title=&#34;Business Rules Expressions&#34;&gt;&lt;/a&gt;Business Rules Expressions&lt;/h2&gt;&lt;p&gt;Our initial business rules from the first Java Batch post were the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;fragile product &amp;#8594; workplace 4&lt;/li&gt;
&lt;li&gt;oversize product &amp;#8594; workplace 3&lt;/li&gt;
&lt;li&gt;line 5 (hall 2) &amp;#8594; workplace 5&lt;/li&gt;
&lt;li&gt;else &amp;#8594; distribute evenly between workplace 1 and 2&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Rule 1 to 3 are very easily translated to the following Jakarta EL expressions:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;entity.packagingWorkplace = entity.fragile ? 4 : entity.packagingWorkplace&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;entity.packagingWorkplace = entity.volume &amp;gt;= 120000000 ? 3 : entity.packagingWorkplace&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;entity.packagingWorkplace = entity.line == 5 ? 5 : entity.packagingWorkplace&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;But the fourth rule is a bit more complicated as we need to save some state (the last workplace
assignment for that case). This can be solved in various ways. I chose to add a counter object
to the context.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; Map&amp;lt;Object, Object&amp;gt; userContext = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;HashMap&lt;/span&gt;&amp;lt;&amp;gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;type&#34;&gt;AtomicInteger&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;counter&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;AtomicInteger&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;elResolver = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;DelegatingMapELResolver&lt;/span&gt;(userContext);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;elContext.addELResolver(elResolver);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;userContext.put(&lt;span class=&#34;string&#34;&gt;&amp;quot;counter&amp;quot;&lt;/span&gt;, counter);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The following expression queries and increments the counter:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;entity.packagingWorkplace = ((counter.getAndIncrement() mod 2) + 1) : entity.packagingWorkplace&amp;quot;)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;But we only want to use this expression as the last resort if no other expression matched.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;entity.packagingWorkplace = entity.packagingWorkplace == NULL ?  ((counter.getAndIncrement() mod 2) + 1) : entity.packagingWorkplace&amp;quot;)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Note: Using the ternary operator “?” is a workaround for the lack of an if&amp;#x2F;else construct in the Jakarta Expression Language.&lt;/p&gt;
&lt;h2 id=&#34;Performance&#34;&gt;&lt;a href=&#34;#Performance&#34; class=&#34;headerlink&#34; title=&#34;Performance&#34;&gt;&lt;/a&gt;Performance&lt;/h2&gt;&lt;p&gt;I was positively surprised by the performance of Expressly. I had a database table with around 850k
records. I read the whole table and executed a single expression on every record. The whole processing
was done in under 15 seconds. Kudos to the developers of Jakarta EL and Expressly.&lt;/p&gt;
&lt;h2 id=&#34;Source-Code&#34;&gt;&lt;a href=&#34;#Source-Code&#34; class=&#34;headerlink&#34; title=&#34;Source Code&#34;&gt;&lt;/a&gt;Source Code&lt;/h2&gt;&lt;p&gt;I have uploaded an &lt;a href=&#34;https://bitbucket.org/m1hael/production-planning/&#34;&gt;example project&lt;/a&gt; at Bitbucket.
Feel free to take a look at it. I added various branches to the Git repository which reflect my
various steps and stops of my journey.&lt;/p&gt;
&lt;h2 id=&#34;Wrap-Up&#34;&gt;&lt;a href=&#34;#Wrap-Up&#34; class=&#34;headerlink&#34; title=&#34;Wrap Up&#34;&gt;&lt;/a&gt;Wrap Up&lt;/h2&gt;&lt;p&gt;Turning the program from hardcoded business rules into using configurable user defined business
rules was much easier than anticipated and the real surprise for me was the very good performance.
Again it shows that Java is a really great programming language with its excellent libraries. One
can achieve so much with so little effort.&lt;/p&gt;
&lt;p&gt;Happy new year! &amp;#127881;&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2025/12/31/2025-12-31-batch-core-maintainability/</guid>
            <title>Batch Core - Maintainability</title>
            <link>http://example.com/2025/12/31/2025-12-31-batch-core-maintainability/</link>
            <category>Java</category>
            <pubDate>Wed, 31 Dec 2025 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;h1 id=&#34;Batch-Core-Maintainability&#34;&gt;&lt;a href=&#34;#Batch-Core-Maintainability&#34; class=&#34;headerlink&#34; title=&#34;Batch Core - Maintainability&#34;&gt;&lt;/a&gt;Batch Core - Maintainability&lt;/h1&gt;&lt;p&gt;So how easy is it to maintain and extend a project which uses &lt;a href=&#34;https://bitbucket.org/m1hael/batch-core&#34;&gt;batch-core&lt;/a&gt;.
Of course that mainly depends on the change. But some frameworks and libraries make it easier and
some harder to integrate changes.&lt;/p&gt;
&lt;p&gt;We take our &lt;em&gt;production planning&lt;/em&gt; example and want to only process the data for a specific
production day.&lt;/p&gt;
&lt;p&gt;So what do we need to do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pass the desired production day to the program (command line parameter)&lt;/li&gt;
&lt;li&gt;get the passed date to the step implementation&lt;/li&gt;
&lt;li&gt;change the SQL to optionally use the passed date&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;Changes&#34;&gt;&lt;a href=&#34;#Changes&#34; class=&#34;headerlink&#34; title=&#34;Changes&#34;&gt;&lt;/a&gt;Changes&lt;/h2&gt;&lt;p&gt;The production day can be passed to the program in various ways as we are using MicroProfile
Config and also have our own &lt;code&gt;ConfigSource&lt;/code&gt; for the program call parameters.&lt;/p&gt;
&lt;figure class=&#34;highlight shell&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;java -cp production-planning.jar:libs/* rpgnextgen.batch.Main start packaging --production-day 2025-12-30&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;In the step implementation we need to add the configuration property.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@Inject&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@ConfigProperty(name = &amp;quot;production-day&amp;quot;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; Optional&amp;lt;LocalDate&amp;gt; productionDay;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;We make this an &lt;code&gt;Optional&lt;/code&gt; because we also want to be able to not pass a production day. And as we are using
the SmallRye implementation of MicroProfile Config we can directly use &lt;code&gt;LocalDate&lt;/code&gt; even though the
MicroProfile Config spec doesn’t support it.&lt;/p&gt;
&lt;p&gt;The SQL only needs a little tweaking in the &lt;code&gt;WHERE&lt;/code&gt; clause to support optionally passing a production day.
If none is passed the parameter is expected to be &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@SqlQuery(&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;    SELECT id, itemId, productionDay, category, line, width, height, depth, fragile&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;    FROM productionPlanning&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;    WHERE (:productionDay IS NULL OR productionDay = :productionDay)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;    &amp;quot;&amp;quot;&amp;quot;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@RegisterBeanMapper(ProductionPlanningEntry.class)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;Stream&amp;lt;ProductionPlanningEntry&amp;gt; &lt;span class=&#34;title function_&#34;&gt;stream&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;(&lt;span class=&#34;meta&#34;&gt;@Bind(&amp;quot;productionDay&amp;quot;)&lt;/span&gt; LocalDate productionDay)&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now we can either pass the production day as a &lt;code&gt;LocalDate&lt;/code&gt; object to the &lt;code&gt;stream&lt;/code&gt; method or pass &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dao.stream(productionDay.orElse(&lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;))&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;And that’s it! &amp;#127881;&lt;/p&gt;
&lt;h2 id=&#34;Wrap-up&#34;&gt;&lt;a href=&#34;#Wrap-up&#34; class=&#34;headerlink&#34; title=&#34;Wrap up&#34;&gt;&lt;/a&gt;Wrap up&lt;/h2&gt;&lt;p&gt;&lt;code&gt;batch-core&lt;/code&gt; did not stand in our way in this change but really helped us. The production day
can be easily added as a parameter to program call (but also as an environment variable as
MicroProfile Config is used). Injecting the production day to the step implementation was also
easy and the adjustment in the SQL had nothing to do with &lt;code&gt;batch-core&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;What we didn’t cover was parameter validation. Passing a not valid date results in an exception
and abnormal termination. That may be a desired result but it depends on your environment.&lt;/p&gt;
&lt;p&gt;So this was a really easy one &amp;#128512; .&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2025/12/30/2025-12-30-batch-core-minimalistic/</guid>
            <title>Batch Core - Minimalistic Example</title>
            <link>http://example.com/2025/12/30/2025-12-30-batch-core-minimalistic/</link>
            <category>Java</category>
            <pubDate>Tue, 30 Dec 2025 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;h1 id=&#34;Batch-Core&#34;&gt;&lt;a href=&#34;#Batch-Core&#34; class=&#34;headerlink&#34; title=&#34;Batch Core&#34;&gt;&lt;/a&gt;Batch Core&lt;/h1&gt;&lt;p&gt;I decided to extract the core classes of my last Java batch example and put them in their own
project. The project is uploaded to &lt;a href=&#34;https://central.sonatype.com/artifact/com.rpgnextgen/batch-core&#34;&gt;Maven Central&lt;/a&gt;. There are two versions of &lt;code&gt;batch-core&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;version 1.x.x &amp;#x3D; JBoss Weld 4.x&lt;/li&gt;
&lt;li&gt;version 2.x.x &amp;#x3D; JBoss Weld 6.x&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So far I mostly used Weld 4 but as I have no conflicting dependencies I wanted to give Weld 6 a try.&lt;/p&gt;
&lt;figure class=&#34;highlight xml&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;dependency&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;groupId&lt;/span&gt;&amp;gt;&lt;/span&gt;com.rpgnextgen&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;groupId&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;artifactId&lt;/span&gt;&amp;gt;&lt;/span&gt;batch-core&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;artifactId&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;version&lt;/span&gt;&amp;gt;&lt;/span&gt;2.0.0&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;version&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/dependen&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;Most-simple-example&#34;&gt;&lt;a href=&#34;#Most-simple-example&#34; class=&#34;headerlink&#34; title=&#34;Most simple example&#34;&gt;&lt;/a&gt;Most simple example&lt;/h2&gt;&lt;p&gt;The simplest example is to just implement &lt;code&gt;WorkflowStep&lt;/code&gt; and be done.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@ApplicationScoped&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@Named(&amp;quot;hello&amp;quot;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;HelloStep&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;TypedWorkflowStep&lt;/span&gt;&amp;lt;Void&amp;gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;meta&#34;&gt;@Override&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Void &lt;span class=&#34;title function_&#34;&gt;execute&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;(Void parameter)&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		System.out.println(&lt;span class=&#34;string&#34;&gt;&amp;quot;Hello World&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;That’s it! No other classes need. No other files needed (besides those for CDI support).&lt;/p&gt;
&lt;p&gt;Note: Don’t forget to setup your project for CDI because your step implementation needs to be a bean.&lt;/p&gt;
&lt;h2 id=&#34;More-icing-on-the-cake&#34;&gt;&lt;a href=&#34;#More-icing-on-the-cake&#34; class=&#34;headerlink&#34; title=&#34;More icing on the cake&#34;&gt;&lt;/a&gt;More icing on the cake&lt;/h2&gt;&lt;p&gt;And as the step implementation is a bean I can inject a configuration value directly into the bean
without any fuzz.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@ApplicationScoped&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@Named(&amp;quot;hello&amp;quot;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;HelloStep&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;TypedWorkflowStep&lt;/span&gt;&amp;lt;Void&amp;gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;meta&#34;&gt;@Inject&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;meta&#34;&gt;@ConfigProperty(name = &amp;quot;name&amp;quot;, defaultValue = &amp;quot;World&amp;quot;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; String name;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;meta&#34;&gt;@Override&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Void &lt;span class=&#34;title function_&#34;&gt;execute&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;(Void parameter)&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		System.out.println(&lt;span class=&#34;string&#34;&gt;&amp;quot;Hello &amp;quot;&lt;/span&gt; + name);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;… or I could inject other beans here. Feel free! :)&lt;/p&gt;
&lt;p&gt;Happy batching!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2025/12/28/2025-12-28-jbatch-streams/</guid>
            <title>JBatch Streams - Refined!</title>
            <link>http://example.com/2025/12/28/2025-12-28-jbatch-streams/</link>
            <category>Java</category>
            <pubDate>Sun, 28 Dec 2025 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;h1 id=&#34;JBatch-Streams-Refined&#34;&gt;&lt;a href=&#34;#JBatch-Streams-Refined&#34; class=&#34;headerlink&#34; title=&#34;JBatch Streams - Refined!&#34;&gt;&lt;/a&gt;JBatch Streams - Refined!&lt;/h1&gt;&lt;p&gt;The last post was about the low cost alternative Java Streams for batch processing. But the stream
processing was pressed into a Jakarta EE Batchlet which has some nice side benefits but also some
overhead which I would like to get rid off in this post.&lt;/p&gt;
&lt;h2 id=&#34;Kick-Jakarta-EE-Batch&#34;&gt;&lt;a href=&#34;#Kick-Jakarta-EE-Batch&#34; class=&#34;headerlink&#34; title=&#34;Kick Jakarta EE Batch&#34;&gt;&lt;/a&gt;Kick Jakarta EE Batch&lt;/h2&gt;&lt;p&gt;First I kicked out Jakarta EE Batch. This was not too difficult and in hindsight the number of
removed libraries and dependencies was surprisingly small.&lt;/p&gt;
&lt;h2 id=&#34;Replacements&#34;&gt;&lt;a href=&#34;#Replacements&#34; class=&#34;headerlink&#34; title=&#34;Replacements&#34;&gt;&lt;/a&gt;Replacements&lt;/h2&gt;&lt;p&gt;But removing the dependencies from the Maven POM was the least effort in this milestone. I now had
to replace some of the functionality Jakarta EE Batch brings to the table.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Batch Properties Injection&lt;/li&gt;
&lt;li&gt;Workflow Configuration&lt;/li&gt;
&lt;li&gt;Workflow Starter&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;Batch-Properties-Injection&#34;&gt;&lt;a href=&#34;#Batch-Properties-Injection&#34; class=&#34;headerlink&#34; title=&#34;Batch Properties Injection&#34;&gt;&lt;/a&gt;Batch Properties Injection&lt;/h3&gt;&lt;p&gt;Instead of having a separate mechanism for injecting properties I used MicroProfile Config for
injecting values. I added a new ConfigSource implementation which delivers the values passed as
batch properties via the command line. The only thing one needs to take care of is using a
&lt;code&gt;Provider&lt;/code&gt; instead of the concrete type because at the time the ConfigSource instance is created
the command line parameters haven’t been evaluated yet.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@Inject&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@ConfigProperty(name = &amp;quot;database&amp;quot;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; Provider&amp;lt;String&amp;gt; database;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;As MicroProfile Config is used for providing injectable values I can override the parameters passed
on the command line with values from a ConfigSource rated higher than the one for the batch properties.&lt;/p&gt;
&lt;p&gt;The ordering is as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;100 : microprofile-config.properties file&lt;/li&gt;
&lt;li&gt;200 : Batch properties&lt;/li&gt;
&lt;li&gt;300 : Environment variables&lt;/li&gt;
&lt;li&gt;400 : Java system properties&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So if every ConfigSource provides a value for the same property the batch properties passed on
the command line would trump the value in the microprofile-config.properties file. The value
from the environment variables ConfigSource would trump the batch properties and the Java system
property would trump them all.&lt;/p&gt;
&lt;h3 id=&#34;Workflow-Configuration&#34;&gt;&lt;a href=&#34;#Workflow-Configuration&#34; class=&#34;headerlink&#34; title=&#34;Workflow Configuration&#34;&gt;&lt;/a&gt;Workflow Configuration&lt;/h3&gt;&lt;p&gt;Jakarta EE Batch supports many batch scenarios and features, but I only intend to support a
simple batch job scenario and don’t need all those additional capabilities.&lt;/p&gt;
&lt;p&gt;The result is a workflow which declares an array of steps that should be executed. So the developer
only needs to implements two interfaces: &lt;code&gt;Workflow&lt;/code&gt; and &lt;code&gt;WorkflowStep&lt;/code&gt;. All declared workflow steps
will be executed sequentially in the specified order.&lt;/p&gt;
&lt;p&gt;Optionally you can also implement only the &lt;code&gt;WorkflowStep&lt;/code&gt; interface if you just only have one step
anyway. No need to also implement a &lt;code&gt;Workflow&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;Workflow-Starter&#34;&gt;&lt;a href=&#34;#Workflow-Starter&#34; class=&#34;headerlink&#34; title=&#34;Workflow Starter&#34;&gt;&lt;/a&gt;Workflow Starter&lt;/h3&gt;&lt;p&gt;Jakarta EE Batch uses XML to define the workflow and also the starting point of the workflow. In
this little batch implementation we are using CDI to mark possible starting points with the &lt;code&gt;Named&lt;/code&gt;
annotation.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@ApplicationScoped&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@Named(&amp;quot;packaging&amp;quot;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;PackagingWorkflow&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;TypedWorkflow&lt;/span&gt;&amp;lt;Void&amp;gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  	&lt;span class=&#34;meta&#34;&gt;@SuppressWarnings(&amp;quot;unchecked&amp;quot;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  	&lt;span class=&#34;meta&#34;&gt;@Override&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  	&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Class&amp;lt;? &lt;span class=&#34;keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;TypedWorkflowStep&lt;/span&gt;&amp;lt;Void&amp;gt;&amp;gt;[] steps() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    		&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;Class&lt;/span&gt;[] &amp;#123; DatabaseInitializer.class, PackagingWorkplaceSelectionStep.class &amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  	&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The workflow name &lt;code&gt;packaging&lt;/code&gt; is passed on the command line as a second parameter to the application.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;java -cp production-planning.jar:libs/* rpgnextgen.batch.Main start packaging --database h2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The batch parameters&amp;#x2F;properties are passed as additional parameters. The parameter name is prefixed
with two hyphens followed by the parameter value.&lt;/p&gt;
&lt;p&gt;The workflow name can also be prefixed with &lt;code&gt;workflow:&lt;/code&gt; which identifies it as a workflow name.
We can also directly start a specific step by providing a step name prefixed with &lt;code&gt;step:&lt;/code&gt;&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;java -cp production-planning.jar:libs/* rpgnextgen.batch.Main start step:packaging-workflow-selection&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The java class for the step must be annotated with &lt;code&gt;Named&lt;/code&gt; and it must also be available as a CDI
bean.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@ApplicationScoped&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@Named(&amp;quot;packaging-workplace-selection&amp;quot;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;PackagingWorkplaceSelectionStep&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;TypedWorkflowStep&lt;/span&gt;&amp;lt;Void&amp;gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;By using CDI and &lt;code&gt;Named&lt;/code&gt; we can package multiple workflows into one application and choose the
workflow at application start by passing the corresponding workflow or step name.&lt;/p&gt;
&lt;h2 id=&#34;Dependencies&#34;&gt;&lt;a href=&#34;#Dependencies&#34; class=&#34;headerlink&#34; title=&#34;Dependencies&#34;&gt;&lt;/a&gt;Dependencies&lt;/h2&gt;&lt;p&gt;We only declared 10 dependencies and many of those dependencies are about the database and with all
the transitive dependencies we are at 32 libraries in total. That is very maintainable compared to
other programming languages where even for the simplest things you got 100(s) of dependencies.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;And the maintainers of the dependencies …?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Apache Software Foundation&lt;/li&gt;
&lt;li&gt;Eclipse Foundation&lt;/li&gt;
&lt;li&gt;IBM&lt;/li&gt;
&lt;li&gt;JBoss (Red Hat)&lt;/li&gt;
&lt;li&gt;OW2 (Non Profit Community)&lt;/li&gt;
&lt;li&gt;SmallRye (Red Hat)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are only 2 libraries which doesn’t belong to any big organization:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Jdbi&lt;/li&gt;
&lt;li&gt;GeanTyRef&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Compare that to the dependency graph of other programming languages.&lt;/p&gt;
&lt;h2 id=&#34;Source-Code&#34;&gt;&lt;a href=&#34;#Source-Code&#34; class=&#34;headerlink&#34; title=&#34;Source Code&#34;&gt;&lt;/a&gt;Source Code&lt;/h2&gt;&lt;p&gt;The source code for this example is available at &lt;a href=&#34;https://bitbucket.org/m1hael/production-planning/tree/streams-only&#34;&gt;Bitbucket&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;Wrap-Up&#34;&gt;&lt;a href=&#34;#Wrap-Up&#34; class=&#34;headerlink&#34; title=&#34;Wrap Up&#34;&gt;&lt;/a&gt;Wrap Up&lt;/h2&gt;&lt;p&gt;What has been achieved by removing Jakarta EE Batch:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;more generic approach to batch processing&lt;/li&gt;
&lt;li&gt;more flexibility&lt;/li&gt;
&lt;li&gt;better runtime performance&lt;/li&gt;
&lt;li&gt;chaining&lt;/li&gt;
&lt;li&gt;less dependencies&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Is it a replacement for Jakarta EE Batch?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Definitely not! Jakarta EE Batch brings so much to the table that it is by far no general
replacement for it.&lt;/p&gt;
&lt;p&gt;But if you want to develop a small batch programm in Java and you don’t need all the bells and
whistles of Jakarta EE Batch then it may be a good alternative.&lt;/p&gt;
&lt;p&gt;Happy streaming!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2025/12/14/2025-12-14-jbatch-alternative/</guid>
            <title>JBatch Alternative on IBM i</title>
            <link>http://example.com/2025/12/14/2025-12-14-jbatch-alternative/</link>
            <category>Java</category>
            <pubDate>Sun, 14 Dec 2025 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;h1 id=&#34;JBatch-Alternative-on-IBM-i&#34;&gt;&lt;a href=&#34;#JBatch-Alternative-on-IBM-i&#34; class=&#34;headerlink&#34; title=&#34;JBatch Alternative on IBM i&#34;&gt;&lt;/a&gt;JBatch Alternative on IBM i&lt;/h1&gt;&lt;p&gt;JBatch (or rather Jakarta EE Batch) is a nice and structured way of implementing a batch job.
It has many features like partitioning a task, dynamic job flow, persisting job execution,
restarting a job, … .&lt;/p&gt;
&lt;p&gt;These are great features but not every batch job needs those features. From time to time you
just need to process a database table. No need for partitioning, dynamic job flow or persisting
the job execution. If the job fails you just run it again (after solving the problem ;-) ).&lt;/p&gt;
&lt;p&gt;In those cases Jakarta EE Batch would be overkill.&lt;/p&gt;
&lt;h2 id=&#34;The-Alternative&#34;&gt;&lt;a href=&#34;#The-Alternative&#34; class=&#34;headerlink&#34; title=&#34;The Alternative&#34;&gt;&lt;/a&gt;The Alternative&lt;/h2&gt;&lt;p&gt;Often the solution is already there. Ready to be discovered and used.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Java Streams&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;Stream&lt;/code&gt; from the &lt;code&gt;java.util.stream&lt;/code&gt; package is an excellent lightweight alternative. It can also
be integrated into a Jakarta EE Batch program (by using a stream in a &lt;code&gt;Batchlet&lt;/code&gt;).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;But JBatch and Streams are fundamentally different!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Depending on the perspective … yes, they are different. But they also try to achieve the same
thing: Run a series of steps to process data.&lt;/p&gt;
&lt;p&gt;A fundamental thing in Java Streams is the concept of data flowing - handed from one execution
step to the next. That is a really nice concept. Jakarta EE Batch follows a different concept:
Input -&amp;gt; Processing -&amp;gt; Output&lt;/p&gt;
&lt;p&gt;The sad thing is that you can only have one processor in this chain of steps.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Only a single processor element may be specified. – Jakarta EE Batch Spec 2.0&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It would be nice if you could chain multiple processors and pass the data from one processor to
the next.&lt;/p&gt;
&lt;p&gt;And that is where streams are much more versatile whereas Jakarta EE Batch has a rather rigid
approach.&lt;/p&gt;
&lt;p&gt;When using streams we need to deal with functions. I won’t deep dive into functions here as it
is a really big topic. We will just scratch those parts which matter for this example.&lt;/p&gt;
&lt;h2 id=&#34;Our-Use-Case&#34;&gt;&lt;a href=&#34;#Our-Use-Case&#34; class=&#34;headerlink&#34; title=&#34;Our Use Case&#34;&gt;&lt;/a&gt;Our Use Case&lt;/h2&gt;&lt;p&gt;Our example will be straight from the manufacturing department. We are producing some goods
and these goods need to be packaged. We have several lines and workplaces for packaging and
depending on the type and size of the product we need to assign it to the corresponding
workplace.&lt;/p&gt;
&lt;p&gt;We have a single table as input which contains all manufactured goods for the next production
days and we have another table which receives the output data.&lt;/p&gt;
&lt;h3 id=&#34;Rules&#34;&gt;&lt;a href=&#34;#Rules&#34; class=&#34;headerlink&#34; title=&#34;Rules&#34;&gt;&lt;/a&gt;Rules&lt;/h3&gt;&lt;p&gt;We have five workplaces for packaging.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Workplace 1 : normal packaging (hall 1)&lt;/li&gt;
&lt;li&gt;Workplace 2 : normal packaging (hall 1)&lt;/li&gt;
&lt;li&gt;Workplace 3 : oversize packaging (hall 1)&lt;/li&gt;
&lt;li&gt;Workpalce 4 : fragile packaging (hall 1)&lt;/li&gt;
&lt;li&gt;Workplace 5 : packaging for production hall 2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So not all goods can be packaged at every workplace. We need to setup rules by which the
goods are assigned to the corresponding workplaces.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;fragile product &amp;#8594; workplace 4&lt;/li&gt;
&lt;li&gt;oversize product &amp;#8594; workplace 3&lt;/li&gt;
&lt;li&gt;line 5 (hall 2) &amp;#8594; workplace 5&lt;/li&gt;
&lt;li&gt;else &amp;#8594; distribute evenly between workplace 1 and 2&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;Input&#34;&gt;&lt;a href=&#34;#Input&#34; class=&#34;headerlink&#34; title=&#34;Input&#34;&gt;&lt;/a&gt;Input&lt;/h2&gt;&lt;p&gt;In Jakarta EE Batch we would implement an &lt;code&gt;ItemReader&lt;/code&gt;. When using streams the input comes
from a &lt;code&gt;java.util.stream.Stream&lt;/code&gt; implementation. Luckily we won’t have to implement this
ourselves.&lt;/p&gt;
&lt;p&gt;We will be using the &lt;a href=&#34;https://jdbi.org/&#34;&gt;Jdbi&lt;/a&gt; project for reading the data from the database.
Jdbi provides a nice feature where you can more or less just write the SQL and it will make a
nice POJO from the data. If we want to fetch a set of data we would just define a &lt;code&gt;java.util.List&lt;/code&gt;
as the return type and we would get all the data in one step. That may be convenient with small
sets of data but when you are processing millions of rows you are running out of memory faster
than you can count to three.&lt;/p&gt;
&lt;p&gt;The solution for this is to use &lt;code&gt;java.util.stream.Stream&lt;/code&gt; as the return type. Using a Stream will
only load the next row of data into memory and is much better when it comes to conserving resources.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@SQLQuery(&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;    SELECT id, itemId, productionDay, category, line, width, height, depth, fragile&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;    FROM productionPlanning&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;&amp;quot;&amp;quot;&amp;quot;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@RegisterBeanMapper(ProductionPlanEntry.class)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;Stream&amp;lt;ProductionPlanEntry&amp;gt; &lt;span class=&#34;title function_&#34;&gt;listPlannedProduction&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;()&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;Processor&#34;&gt;&lt;a href=&#34;#Processor&#34; class=&#34;headerlink&#34; title=&#34;Processor&#34;&gt;&lt;/a&gt;Processor&lt;/h2&gt;&lt;p&gt;We got the stream we want to operate on. The main methods we need from a &lt;code&gt;Stream&lt;/code&gt; object are &lt;code&gt;map&lt;/code&gt;,
&lt;code&gt;filter&lt;/code&gt; and &lt;code&gt;collect&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;filter&lt;/code&gt; : We get an object of class x. Our function determines if the object will be dropped from
the stream. Just return &lt;code&gt;false&lt;/code&gt; for dropping the object from the stream.&lt;br&gt;&lt;code&gt;map&lt;/code&gt; : We get an object of class x. We can modify the received object to our liking. Our function
can freely decide what to return. It can be the same object, another object (even of another
class) or null.&lt;br&gt;&lt;code&gt;collect&lt;/code&gt; : This is a terminal operation which means that this function will receive all objects
from the stream and process them in this final step. There is no further processing.&lt;/p&gt;
&lt;p&gt;We can create a function for each rule we want to implement. But as the rules are very miniscule
we just put them all into one function. We can use anonymous functions or implement them as a class.&lt;/p&gt;
&lt;p&gt;There are multiple Java interface for functions. Here are the most common ones:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;java.util.function.Function&lt;/code&gt; : Accepts an object and returns an object. The passed and returned object doesn’t need to be the same object or even of the same class. This can be used by the &lt;code&gt;map&lt;/code&gt; method of a &lt;code&gt;Stream&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;java.util.function.Predicate&lt;/code&gt; : Accepts an object and returns a boolean result. This can be used by the &lt;code&gt;filter&lt;/code&gt; method of a &lt;code&gt;Stream&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are many more interface (like &lt;code&gt;Consumer&lt;/code&gt;, &lt;code&gt;Supplier&lt;/code&gt;, …) but those are of no interest to us
for this example.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;PackagingWorkplaceSelector&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;Function&lt;/span&gt;&amp;lt;ProductionPlanningEntry, ProductionPlanningEntry&amp;gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;type&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;OVERSIZED&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;120000000l&lt;/span&gt;; &lt;span class=&#34;comment&#34;&gt;// volume in millimeter³&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;type&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;lastDefaultPackagingWorkplace&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;meta&#34;&gt;@Override&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; ProductionPlanningEntry &lt;span class=&#34;title function_&#34;&gt;apply&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;(ProductionPlanningEntry entry)&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		&lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (entry.isFragile())&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;			entry.setPackagingWorkplace(&lt;span class=&#34;number&#34;&gt;4&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		&lt;span class=&#34;keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (isOversized(entry))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;			entry.setPackagingWorkplace(&lt;span class=&#34;number&#34;&gt;3&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		&lt;span class=&#34;keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (entry.getLine() == &lt;span class=&#34;number&#34;&gt;5&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;			entry.setPackagingWorkplace(&lt;span class=&#34;number&#34;&gt;5&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		&lt;span class=&#34;keyword&#34;&gt;else&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;			entry.setPackagingWorkplace(getNextDefaultWorkplace());&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; entry;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; Integer &lt;span class=&#34;title function_&#34;&gt;getNextDefaultWorkplace&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;()&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		lastDefaultPackagingWorkplace = lastDefaultPackagingWorkplace == &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt; ? &lt;span class=&#34;number&#34;&gt;2&lt;/span&gt; : &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; lastDefaultPackagingWorkplace;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;type&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;title function_&#34;&gt;isOversized&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;(ProductionPlanningEntry entry)&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; entry.getVolume() &amp;gt;= OVERSIZED;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;Output&#34;&gt;&lt;a href=&#34;#Output&#34; class=&#34;headerlink&#34; title=&#34;Output&#34;&gt;&lt;/a&gt;Output&lt;/h2&gt;&lt;p&gt;Writing the data to a file or table can be done in a &lt;em&gt;mapping&lt;/em&gt; function. We use Jdbi for writing
the data to an output table.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@SqlUpdate(&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;    INSERT INTO productionPlanningPackaging (itemId, productionDay, packagingWorkplace)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;    VALUES(:itemId, :productionDay, :packagingWorkplace)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;    &amp;quot;&amp;quot;&amp;quot;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title function_&#34;&gt;insertPackagingWorkplace&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;(&lt;span class=&#34;meta&#34;&gt;@BindBean&lt;/span&gt; ProductionPlanningEntry entry)&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;Putting-it-all-together&#34;&gt;&lt;a href=&#34;#Putting-it-all-together&#34; class=&#34;headerlink&#34; title=&#34;Putting it all together&#34;&gt;&lt;/a&gt;Putting it all together&lt;/h2&gt;&lt;p&gt;Putting all those parts together may look like this:&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;ProductionWorkflow&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;Batchlet&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;type&#34;&gt;Logger&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;logger&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; Logger.getLogger(&lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.getClass().getName());&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;meta&#34;&gt;@Inject&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;meta&#34;&gt;@BatchProperty(name = &amp;quot;database&amp;quot;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; String database;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;meta&#34;&gt;@Override&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; String &lt;span class=&#34;title function_&#34;&gt;process&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;throws&lt;/span&gt; Exception &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		&lt;span class=&#34;type&#34;&gt;Jdbi&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;jdbi&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; CDI.current().select(Jdbi.class, NamedLiteral.of(database)).get();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		&lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt; (&lt;span class=&#34;type&#34;&gt;Handle&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;handle&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; jdbi.open()) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;			&lt;span class=&#34;type&#34;&gt;ProductionPlanningDao&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;dao&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; handle.attach(ProductionPlanningDao.class);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;			Map&amp;lt;Integer, List&amp;lt;ProductionPlanningEntry&amp;gt;&amp;gt; groupedWorkplaces = dao.stream()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    .map(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;VolumeCalculator&lt;/span&gt;())&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    .map(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;PackagingWorkplaceSelector&lt;/span&gt;())&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    .map(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;PackagingWorkplaceWriter&lt;/span&gt;(dao))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    .collect(Collectors.groupingBy(ProductionPlanningEntry::getPackagingWorkplace));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;for&lt;/span&gt; (Integer key : groupedWorkplaces.keySet()) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                logger.info(key + &lt;span class=&#34;string&#34;&gt;&amp;quot;: &amp;quot;&lt;/span&gt; + groupedWorkplaces.get(key).size());&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;ok&amp;quot;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;meta&#34;&gt;@Override&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title function_&#34;&gt;stop&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;throws&lt;/span&gt; Exception &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;span class=&#34;comment&#34;&gt;// nothing to do&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Output:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;2025-12-22 10:48:02 INFORMATION rpgnextgen.packaging.ProductionWorkflow: 1: 427&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2025-12-22 10:48:02 INFORMATION rpgnextgen.packaging.ProductionWorkflow: 2: 426&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2025-12-22 10:48:02 INFORMATION rpgnextgen.packaging.ProductionWorkflow: 3: 36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2025-12-22 10:48:02 INFORMATION rpgnextgen.packaging.ProductionWorkflow: 4: 100&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2025-12-22 10:48:02 INFORMATION rpgnextgen.packaging.ProductionWorkflow: 5: 11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;I planned to support the use of different databases in this demo and thus the database gets injected,
see batch property &lt;code&gt;database&lt;/code&gt;. Depending on the database we get the corresponding Jdbi provider.&lt;/p&gt;
&lt;p&gt;The different functions are chained in the stream and as a final step the number of products for
each workplace are summed up.&lt;/p&gt;
&lt;h2 id=&#34;Miscellaneous&#34;&gt;&lt;a href=&#34;#Miscellaneous&#34; class=&#34;headerlink&#34; title=&#34;Miscellaneous&#34;&gt;&lt;/a&gt;Miscellaneous&lt;/h2&gt;&lt;p&gt;Sometimes we have processed all objects in the stream in a &lt;em&gt;filter&lt;/em&gt; or &lt;em&gt;mapping&lt;/em&gt; function and
just want to just drop all objects after our last function. &lt;code&gt;filter&lt;/code&gt; and &lt;code&gt;map&lt;/code&gt; are intermediate
operations. We need to make a final call on the stream with a terminal operation. There is no
terminal operation like &lt;code&gt;sink&lt;/code&gt;, &lt;code&gt;drop&lt;/code&gt; or &lt;code&gt;dump&lt;/code&gt; but you can use &lt;code&gt;count&lt;/code&gt; which is probably the
fastest terminal operation.&lt;/p&gt;
&lt;p&gt;If we want to make sure that no &lt;code&gt;null&lt;/code&gt; values are passed down the stream we can use
&lt;code&gt;stream.filter(Objects::notNull)&lt;/code&gt; to filter out all &lt;code&gt;null&lt;/code&gt; values.&lt;/p&gt;
&lt;h2 id=&#34;Source-Code&#34;&gt;&lt;a href=&#34;#Source-Code&#34; class=&#34;headerlink&#34; title=&#34;Source Code&#34;&gt;&lt;/a&gt;Source Code&lt;/h2&gt;&lt;p&gt;The source code for this example is available at &lt;a href=&#34;https://bitbucket.org/m1hael/production-planning&#34;&gt;Bitbucket&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;Wrap-Up&#34;&gt;&lt;a href=&#34;#Wrap-Up&#34; class=&#34;headerlink&#34; title=&#34;Wrap Up&#34;&gt;&lt;/a&gt;Wrap Up&lt;/h2&gt;&lt;p&gt;The JBatch example project was a good starting point for using streams in a batch process.
We get a lot of benefits from it like CDI and argument processing and it works but we could make
it even easier and more straightforward if we drop the Jakarta EE stuff and just use the streams
(and perhaps Weld for CDI ;-) ). This sounds like a topic for the next blog post ;-).&lt;/p&gt;
&lt;p&gt;… and if you are interested in using streams in RPG there is actually an implementation at
&lt;a href=&#34;https://bitbucket.org/m1hael/stream&#34;&gt;Bitbucket&lt;/a&gt;. I have written a
&lt;a href=&#34;https://blog.rpgnextgen.com/2020/05/08/2020-05-08-streaming-api/&#34;&gt;post&lt;/a&gt; about it a while ago.&lt;/p&gt;
&lt;p&gt;Happy streaming!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2025/06/29/2025-06-22-qrcodes/</guid>
            <title>QR Codes on IBM i</title>
            <link>http://example.com/2025/06/29/2025-06-22-qrcodes/</link>
            <category>RPG</category>
            <pubDate>Sun, 29 Jun 2025 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;h1 id=&#34;QR-Codes-on-IBM-i&#34;&gt;&lt;a href=&#34;#QR-Codes-on-IBM-i&#34; class=&#34;headerlink&#34; title=&#34;QR Codes on IBM i&#34;&gt;&lt;/a&gt;QR Codes on IBM i&lt;/h1&gt;&lt;p&gt;QR Codes are a very convenient solution for many use cases. In contrast to barcodes a very
broad range of data can be encoded into a QR Code. Today you will encounter many QR codes
which have a URL encoded into it which can be scanned with your smartphone to open a web site.&lt;/p&gt;
&lt;p&gt;QR codes are 2 dimensional in constrast to barcodes which just use one dimension.&lt;/p&gt;
&lt;p&gt;Another difference to barcodes is the size of a QR codes. The size of a QR code depends on some
factors:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Size of encoded data&lt;/li&gt;
&lt;li&gt;Type of encoded data&lt;/li&gt;
&lt;li&gt;Level or error correction&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For a better explaination and more details on this see
&lt;a href=&#34;https://www.qrcode.com/en/about/version.html&#34;&gt;Information capacity and versions of the QR Code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And if this is not enough motivation to learn something about QR codes and how to create them
on IBM i natively … have you ever heard of the “GS1 Sunrise 2027 Initiative”?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Transitioning to 2D barcodes for POS is a multi-step process. The GS1 US
Sunrise 2027 initiative lays out a plan to help ensure 2D barcodes will be
scanned and processed at any retail POS by the end of 2027.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Quote from the &lt;a href=&#34;https://www.gs1us.org/&#34;&gt;GS1&lt;/a&gt; website.&lt;/p&gt;
&lt;p&gt;And by 2D barcodes they mean QR codes and data matrix.&lt;/p&gt;
&lt;h2 id=&#34;Native-Support&#34;&gt;&lt;a href=&#34;#Native-Support&#34; class=&#34;headerlink&#34; title=&#34;Native Support&#34;&gt;&lt;/a&gt;Native Support&lt;/h2&gt;&lt;p&gt;There is native support to print QR codes on IBM i. But this doesn’t help if you need to output
the QR code to another medium like the web or a stream file.&lt;/p&gt;
&lt;p&gt;There are solutions but none of them are freely available and directly usable from the ILE
environment. Most incorporate other programming languages like Python or Node.js.&lt;/p&gt;
&lt;p&gt;And don’t even start with any kind of printer files … yak.&lt;/p&gt;
&lt;p&gt;But a free ILE solution is not that hard to implement. There are a lot open source libraries
available on the internet which can help in generating a QR code and can be used in the ILE
environment.&lt;/p&gt;
&lt;h2 id=&#34;The-Steps&#34;&gt;&lt;a href=&#34;#The-Steps&#34; class=&#34;headerlink&#34; title=&#34;The Steps&#34;&gt;&lt;/a&gt;The Steps&lt;/h2&gt;&lt;p&gt;Generating a QR code is more or less done in two steps.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;calculate which blocks needed to be black &amp;#x2F; white&lt;/li&gt;
&lt;li&gt;draw the blocks (on a canvas like the browser window or into a stream file)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For step 1 we use the C library &lt;a href=&#34;https://github.com/fukuchi/libqrencode&#34;&gt;libqrencode&lt;/a&gt; from
Kentaro Fukuchi available as an open source library on GitHub.&lt;/p&gt;
&lt;p&gt;The library is mostly C89 compatible and therefore can be compiled as ILE modules and bound
to an ILE service program. There is a precompiled version available in the RPM repository at
&lt;a href=&#34;https://repo.rpgnextgen.com/&#34;&gt;repo.rpgnextgen.com&lt;/a&gt; and installable via
&lt;a href=&#34;https://bitbucket.org/m1hael/ipkg&#34;&gt;iPKG&lt;/a&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install libqrencode&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The RPG prototypes can also be installed with iPKG.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install &amp;#x27;libqrencode-devel&amp;#x27; loc(&amp;#x27;/home/mihael/include&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Getting the first step done in an RPG program is not that difficult.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc main;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    dcl-ds qrCode likeds(qrcode_t) based(qrCodePtr);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    dcl-s input varchar(100) ccsid(*UTF8);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    input = &amp;#x27;Mihael Schmidt&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    qrCodePtr = QRcode_encodeData(%len(input) : %addr(input : *data) : 0 : 0);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    on-exit;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        QRcode_free(qrCodePtr);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The C function &lt;code&gt;QRcode_encodeData&lt;/code&gt; takes some raw data and encodes it into the QR code.
For that it needs to know how large the data is (size) and where data starts (address of
the variable, data part).&lt;/p&gt;
&lt;p&gt;The parameter 3 and 4 are QR code specific. The &lt;code&gt;version&lt;/code&gt; (parameter 3) defines the number
of “modules” used in the QR code. Modules are the blocks in the QR code. More modules &amp;#x3D; more
data can be encoded. The value &lt;code&gt;0&lt;/code&gt; means the minimum number of modules for the passed data
will be used.&lt;/p&gt;
&lt;p&gt;Parameter 4 - &lt;code&gt;ecLevel&lt;/code&gt; - defines the level or error correction. The higher the level the
less data can be encoded into QR code with the same &lt;code&gt;version&lt;/code&gt;. The available values range
from 0 to 3 where 0 is the lowest level of error correction and 3 the highest.&lt;/p&gt;
&lt;p&gt;The library &lt;code&gt;libqrencode&lt;/code&gt; offers many more functions to encode data but this is the easiest
one to use.&lt;/p&gt;
&lt;h2 id=&#34;Output-on-Screen&#34;&gt;&lt;a href=&#34;#Output-on-Screen&#34; class=&#34;headerlink&#34; title=&#34;Output on Screen&#34;&gt;&lt;/a&gt;Output on Screen&lt;/h2&gt;&lt;p&gt;We can output the data on the screen with just some lines of code. We take the variable
&lt;code&gt;qrCode&lt;/code&gt; from the previous block of code and pass it to the procedure &lt;code&gt;drawOnConsole&lt;/code&gt;. The
filled modules will be drawn as an &lt;code&gt;X&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc drawOnConsole;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    dcl-pi *n;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        qrCode likeds(qrcode_t);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    end-pi;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    dcl-s line varchar(50);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    dcl-s x int(10);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    dcl-s y int(10);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    dcl-s qrBlockData char(1) based(qrBlockPtr);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    qrBlockPtr = qrCode.data;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    for x = 1 to qrCode.width;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        clear line;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        for y = 1 to qrcode.width;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            if (%bitand(qrBlockData : QRCODE_BLACK) = QRCODE_BLACK);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                line += &amp;#x27;X&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            else;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                line += &amp;#x27; &amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            endif;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            qrBlockPtr += 1;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        endfor;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        dsply line;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    endfor;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The returned QR code data from &lt;code&gt;QRcode_encodeData&lt;/code&gt; is really just a matrix with some bit flags
switched on or off, f. e. for the fill state of a block.&lt;/p&gt;
&lt;p&gt;From the libqrencode documentation:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;// MSB 76543210 LSB&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//     |||||||`- 1=black/0=white&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//     ||||||`-- 1=ecc/0=data code area&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//     |||||`--- format information&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//     ||||`---- version information&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//     |||`----- timing pattern&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//     ||`------ alignment pattern&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//     |`------- finder pattern and separator&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//     `-------- non-data modules (format, timing, etc.)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;To test if a block is filled (black) or not we just need to define some constants and test it with
the &lt;code&gt;bitand&lt;/code&gt; built-in function. The QR code data structure contains the used width which is the
number of modules in one row. The rows of the QR code are contained in the &lt;code&gt;data&lt;/code&gt; subfield of the
data structure. The data of each row is immediately followed by the data of the next row. There is
no separator. But we got the width of each row so we can compute the start and end of each row.&lt;/p&gt;
&lt;p&gt;To get the data for each module (block) we kind of dynamically overlay the QR code data with a
character field with a length of just 1.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;qrCode.data&lt;/code&gt; is just a pointer to the data and not the data itself. By assigning the data pointer
to the pointer the &lt;code&gt;char(1)&lt;/code&gt; field is based on the field holds the first module data. And just by
incrementing this pointer (&lt;code&gt;qrBlockPtr&lt;/code&gt;) by one we can move the field to the next module information.&lt;/p&gt;
&lt;p&gt;The output looks something like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;DSPLY  XXXXXXX   X X XXXXXXX&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY  X     X X X X X     X&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY  X XXX X X XX  X XXX X&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY  X XXX X     X X XXX X&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY  X XXX X XXX X X XXX X&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY  X     X XXX   X     X&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY  XXXXXXX X X X XXXXXXX&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY          X  X&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY  XX X  XX  X X XXX XX&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY   X X   XX X XX   XXXX&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY      XXXXXXXXX  X XX X&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY  X   XX X  X   XX X  X&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY  XX X XXX   XX   X  X&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY          X  XX  X XX&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY  XXXXXXX X X XXX XXXX&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY  X     X        X   XX&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY  X XXX X  XX  X XX&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY  X XXX X XXX XX XX  XX&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY  X XXX X  XXXX XXXXX X&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY  X     X XX   X  X&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DSPLY  XXXXXXX XX    XXXX X&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;This is a QR code with 21 x 21 modules (blocks) and equals the QR code version &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Not too bad :)&lt;/p&gt;
&lt;h2 id=&#34;Output-to-Image&#34;&gt;&lt;a href=&#34;#Output-to-Image&#34; class=&#34;headerlink&#34; title=&#34;Output to Image&#34;&gt;&lt;/a&gt;Output to Image&lt;/h2&gt;&lt;p&gt;But in the modern world we seldom need to output a QR codes onto a console screen. But what is much
more common is the output to an image file f. e. in PNG format.&lt;/p&gt;
&lt;p&gt;There is no support out-of-the-box available in the ILE environment for dynamically creating PNG images
so we need to extend our toolbox by another software library.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.libpng.org/pub/png/libpng.html&#34;&gt;libpng&lt;/a&gt; is a good candidate as it boasts to be written in
ANSI C (C89) but the project is quite big and I only need to output some black and white blocks. So I
keep looking for another candidate.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/lvandeve/lodepng&#34;&gt;lodepng&lt;/a&gt; is a very small library which can be easily embedded in
a project as it only consists of a single header file and a single source file.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In addition to C++, LodePNG also supports ANSI C (C89), with all the same functionality:
C++ only adds extra convenience API.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That sounds promising!&lt;/p&gt;
&lt;p&gt;And the project really delivered! It hasn’t promised too much. Though I had to work around some
CCSID problems (as always ;-), it compiled out of the box (not counting the CCSID problems) and
we can install it and the corresponding copybooks with iPKG.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install lodepng&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ipkg install &amp;#x27;lodepng-devel&amp;#x27; loc(&amp;#x27;/home/mihael/include&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Note: You can also compile all these software libraries by yourself and don’t have to use iPKG. You can
find the libraries with their Makefile at &lt;a href=&#34;https://bitbucket.org/m1hael/&#34;&gt;my Bitbucket page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So far we can create the QR code data and know which blocks are filled and which are not filled.
Now we need to output that data to a PNG image.&lt;/p&gt;
&lt;p&gt;First we need to convert the QR code data to pixel data. The raw pixel data is a block of memory
where each pixel consists of 3 or 4 values (RBG - Red Green Blue or RBGA - Red Green Blue Alpha).
Each value needs to hold a number from 0 to 255. We declare this as &lt;code&gt;uns(3)&lt;/code&gt; – a one byte unsigned
integer. So our block of memory has the size of &lt;code&gt;image width&lt;/code&gt; x &lt;code&gt;image height&lt;/code&gt; x 4 (we use RGBA).&lt;/p&gt;
&lt;p&gt;Now we just need to loop through our QR code data and create pixel data from each module data
(which is too lengthy to show here). The raw pixel data can now be passed to a C function from
the &lt;code&gt;lodepng&lt;/code&gt; project.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;lodepng_encode_file(filename : pixels : imageWidth : imageHeight : 6 : 8);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Most parameters are self describing. Only the 5th and 6th parameter needs some explaination.&lt;/p&gt;
&lt;p&gt;The 5th parameter defines the color type used in the image, 6 &amp;#x3D; RGBA.&lt;/p&gt;
&lt;p&gt;The 6th parameter defines the color depth in bit. The range of values depend on the used color type.
Valid values for the color type RBGA are either 8 or 16.&lt;/p&gt;
&lt;p&gt;This whole process has been wrapped into a nice procedure in my project
&lt;a href=&#34;https://bitbucket.org/m1hael/qrtools/&#34;&gt;qrtools&lt;/a&gt; which can be installed via iPKG.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install qrtools&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ipkg install &amp;#x27;qrtools-devel&amp;#x27; loc(&amp;#x27;/home/mihael/include&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;And the usage of the procedure looks like this.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc main;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    dcl-s input varchar(100) ccsid(*utf8);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    input = &amp;#x27;Mihael Schmidt&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    qrtools_toPng(&amp;#x27;/home/mihael/qrcode.png&amp;#x27; : %addr(input : *data) : %len(input) : 200);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;In this example the created image has a size of 200 x 200 px. The QR code is automatically scaled
to the maximum size inside the given image dimension. Any not used space in the image will be
drawn as 100% transparent pixels.&lt;/p&gt;
&lt;p&gt;Easy peasy! ;-)&lt;/p&gt;
&lt;h2 id=&#34;Wrap-Up&#34;&gt;&lt;a href=&#34;#Wrap-Up&#34; class=&#34;headerlink&#34; title=&#34;Wrap Up&#34;&gt;&lt;/a&gt;Wrap Up&lt;/h2&gt;&lt;p&gt;Now we can natively create QR codes in our beloved ILE environment without having to switch to
any environment which is not integrated as well as our polyglot environment.&lt;/p&gt;
&lt;p&gt;Currently the project &lt;em&gt;qrtools&lt;/em&gt; covers only the creation of a PNG image. Possibly other output
formats could be SVG, EPS, JPEG. This might get implemented depending on the feedback.&lt;/p&gt;
&lt;p&gt;Demand determines supply. ;-)&lt;/p&gt;
&lt;p&gt;Happy QR (en)coding!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2025/04/15/2025-04-15-iledocs-page-builder/</guid>
            <title>ILEDocs ... the 3rd take ... action!</title>
            <link>http://example.com/2025/04/15/2025-04-15-iledocs-page-builder/</link>
            <category>RPG</category>
            <pubDate>Tue, 15 Apr 2025 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;h1 id=&#34;ILEDocs-…-the-3rd-take-…-action&#34;&gt;&lt;a href=&#34;#ILEDocs-…-the-3rd-take-…-action&#34; class=&#34;headerlink&#34; title=&#34;ILEDocs … the 3rd take … action!&#34;&gt;&lt;/a&gt;ILEDocs … the 3rd take … action!&lt;/h1&gt;&lt;p&gt;ILEDocs is now in its third version and implementation. It started in RPG then transitioned to
Java and now arrived in the Node.js world. It differentiates from its predecessor in that it now
produces static HTML documents (again) instead of being a dynamic web application.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;So what does it do and what has changed?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It still does what it should do: producing an API documentation of the programs and service
programs, be it RPG or CL.&lt;/p&gt;
&lt;p&gt;You still comment a program, module or procedure with a comment block like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;///&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// List order entries&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// Returns all order entries for the specified order.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// @param Order number&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// @return List of order entries, see order_entry_t&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;///&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;And the output is still HTML pages.&lt;/p&gt;
&lt;p&gt;Though there are some new features added in this implementation.&lt;/p&gt;
&lt;h2 id=&#34;New-Features&#34;&gt;&lt;a href=&#34;#New-Features&#34; class=&#34;headerlink&#34; title=&#34;New Features&#34;&gt;&lt;/a&gt;New Features&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No extra software required:&lt;/strong&gt; Unlike the Java version, it doesn’t need any additional
software — no database or application server. Just the program and the source code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-platform compatibility:&lt;/strong&gt; It doesn’t have to run on IBM i, though it can, and doing so
may have some advantages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multiple page generation:&lt;/strong&gt; You can generate multiple HTML pages from a single source code base.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optional overview page:&lt;/strong&gt; If the content is split across multiple HTML pages, an overview page
can optionally be generated.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JSON output:&lt;/strong&gt; A JSON document containing the generated pages can be created for further
programmatic processing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom templates:&lt;/strong&gt; The HTML templates can easily be replaced with your own custom set.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;Requirements&#34;&gt;&lt;a href=&#34;#Requirements&#34; class=&#34;headerlink&#34; title=&#34;Requirements&#34;&gt;&lt;/a&gt;Requirements&lt;/h2&gt;&lt;p&gt;As this is a Node.js application you need to install the Node.js runtime (version &amp;gt;&amp;#x3D; 14).&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;yum install nodejs20&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;Installation&#34;&gt;&lt;a href=&#34;#Installation&#34; class=&#34;headerlink&#34; title=&#34;Installation&#34;&gt;&lt;/a&gt;Installation&lt;/h2&gt;&lt;p&gt;The next step is to download the repository of the &lt;a href=&#34;https://bitbucket.org/m1hael/iledocs-page-builder/&#34;&gt;project&lt;/a&gt;
into the IFS or clone the repository in the IFS.&lt;/p&gt;
&lt;p&gt;To install the dependencies of this Node.js applications you need to execute&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;npm install&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;After having the dependencies installed the application can be built.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;npm run build&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;Configuration&#34;&gt;&lt;a href=&#34;#Configuration&#34; class=&#34;headerlink&#34; title=&#34;Configuration&#34;&gt;&lt;/a&gt;Configuration&lt;/h2&gt;&lt;p&gt;The configuration file defines what will be parsed and how&amp;#x2F;where the output will be generated.
The format of the file is JSON.&lt;/p&gt;
&lt;figure class=&#34;highlight json&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;project&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;project name&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;title used in templates&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;output&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;path to the output directory&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;suffix&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;suffix of the rendered pages, f. e. html&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;templates&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;path to the templates directory&amp;quot;&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;overview&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;string&#34;&gt;&amp;quot;index&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;index_json&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;source&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;path to the RPG or CL source code stream file&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;section&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h3 id=&#34;Section&#34;&gt;&lt;a href=&#34;#Section&#34; class=&#34;headerlink&#34; title=&#34;Section&#34;&gt;&lt;/a&gt;Section&lt;/h3&gt;&lt;p&gt;Each section can be configured as follows:&lt;/p&gt;
&lt;figure class=&#34;highlight json&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;id&amp;quot;&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;internal section id&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;name&amp;quot;&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;section name used in template&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;startsWith&amp;quot;&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;including symbols starting with this string (optional)&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;symbols&amp;quot;&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;symbols included by name (optional)&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;default&amp;quot;&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;true&lt;/span&gt;&lt;/span&gt; | &lt;span class=&#34;literal&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;false&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;order&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; pageOrder&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;descriptionAsHeader&amp;quot;&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;true&lt;/span&gt;&lt;/span&gt; | &lt;span class=&#34;literal&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;false&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;filename&amp;quot;&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;page output filename&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If the project has defined a default section every symbol which cannot be mapped to a section
will be mapped to the default section.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;order&lt;/code&gt; attribute defines the order&amp;#x2F;index in which the section is listed in the overview page.&lt;/p&gt;
&lt;p&gt;Each page may have a block of text at the head of the page. If the text should be the ILEDocs
description from the module then set &lt;code&gt;descriptionAsHeader&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; in the section config.&lt;/p&gt;
&lt;h2 id=&#34;Start&#34;&gt;&lt;a href=&#34;#Start&#34; class=&#34;headerlink&#34; title=&#34;Start&#34;&gt;&lt;/a&gt;Start&lt;/h2&gt;&lt;p&gt;The application can be started with &lt;code&gt;npm start&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The project configuration file can be passed as a parameter to &lt;code&gt;npm start&lt;/code&gt;:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;npm start project-config-myapp.json&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;By default the output will be placed into the folder &lt;code&gt;pages&lt;/code&gt; if not configured otherwise.&lt;/p&gt;
&lt;h2 id=&#34;Debugging&#34;&gt;&lt;a href=&#34;#Debugging&#34; class=&#34;headerlink&#34; title=&#34;Debugging&#34;&gt;&lt;/a&gt;Debugging&lt;/h2&gt;&lt;p&gt;The ILEDocs Page Builder project uses the &lt;code&gt;debug&lt;/code&gt; package. To get the debugging output you just
need to activate it by setting the environment variable &lt;code&gt;DEBUG&lt;/code&gt; to &lt;code&gt;iledocs:*&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight shell&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;export DEBUG=iledocs:*&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;or in Powershell&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;$env:DEBUG=&amp;quot;iledocs:*&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;To enable debugging output for single components see the list of defined logging namespaces
for the application in the README of the project.&lt;/p&gt;
&lt;h2 id=&#34;Example&#34;&gt;&lt;a href=&#34;#Example&#34; class=&#34;headerlink&#34; title=&#34;Example&#34;&gt;&lt;/a&gt;Example&lt;/h2&gt;&lt;p&gt;The JSON and XML library &lt;a href=&#34;https://github.com/sitemule/noxDB&#34;&gt;noxDB&lt;/a&gt; is a great example. The
prototypes, constants and data structures of this project are all placed in a single stream
file. We split the content up into multiple pages.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Basics&lt;/li&gt;
&lt;li&gt;Constants&lt;/li&gt;
&lt;li&gt;Variables&lt;/li&gt;
&lt;li&gt;Serializer&lt;/li&gt;
&lt;li&gt;SQL&lt;/li&gt;
&lt;li&gt;Generator and Parser&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For each page the associated exported symbols which are going to go into that page are defined
like this&lt;/p&gt;
&lt;figure class=&#34;highlight json&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;variables&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;Variables&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;symbols&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;string&#34;&gt;&amp;quot;jx_DelimiterDS&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;jx_iterator&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;or we can select all exported symbols which start with a given string:&lt;/p&gt;
&lt;figure class=&#34;highlight json&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;constants&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;Constants&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;startsWith&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;JX_&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;attr&#34;&gt;&amp;quot;order&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;To add an overview page to the generation process we add the configuration entry &lt;code&gt;overview&lt;/code&gt; as a
top level attribute in the configuration JSON:&lt;/p&gt;
&lt;figure class=&#34;highlight json&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;attr&#34;&gt;&amp;quot;overview&amp;quot;&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;string&#34;&gt;&amp;quot;index&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;page&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Note: You can see the &lt;a href=&#34;https://bitbucket.org/m1hael/iledocs-page-builder/src/master/project-config-noxdb.json&#34;&gt;full configuration file&lt;/a&gt;
for noxDB at the ILEDocs Page Builder repository.&lt;/p&gt;
&lt;h2 id=&#34;Limitations&#34;&gt;&lt;a href=&#34;#Limitations&#34; class=&#34;headerlink&#34; title=&#34;Limitations&#34;&gt;&lt;/a&gt;Limitations&lt;/h2&gt;&lt;p&gt;Currently only a single stream file is parsed for generating the documentation. It would be nice
to parse multiple stream files in the process.&lt;/p&gt;
&lt;h2 id=&#34;Real-World-Example&#34;&gt;&lt;a href=&#34;#Real-World-Example&#34; class=&#34;headerlink&#34; title=&#34;Real World Example&#34;&gt;&lt;/a&gt;Real World Example&lt;/h2&gt;&lt;p&gt;All documentation available on &lt;a href=&#34;https://iledocs.rpgnextgen.com/&#34;&gt;iledocs.rpgnextgen.com&lt;/a&gt; has been
generated by ILEDocs.&lt;/p&gt;
&lt;h2 id=&#34;Wrap-Up&#34;&gt;&lt;a href=&#34;#Wrap-Up&#34; class=&#34;headerlink&#34; title=&#34;Wrap Up&#34;&gt;&lt;/a&gt;Wrap Up&lt;/h2&gt;&lt;p&gt;It has never been easier to document programs and service programs and integrate the process into
a build pipeline which also makes the publishing of the documentation very easy.&lt;/p&gt;
&lt;p&gt;For any further information on ILEDocs Page Builder please take a look at README file in the
&lt;a href=&#34;https://bitbucket.org/m1hael/iledocs-page-builder&#34;&gt;project repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy documenting!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2025/01/19/2025-01-19-jdbi-streaming-rowreducer/</guid>
            <title>Jdbi, Streaming and Row Reducer</title>
            <link>http://example.com/2025/01/19/2025-01-19-jdbi-streaming-rowreducer/</link>
            <category>Java</category>
            <pubDate>Sun, 19 Jan 2025 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;Streaming data from a Java web service with JAX-RS is pretty straight forward as we have seen
in one of my previous posts. We can utilize the &lt;code&gt;java.util.stream.Stream&lt;/code&gt; interface and its
implementations for this.&lt;/p&gt;
&lt;p&gt;And streaming data from a database with &lt;a href=&#34;https://www.jdbi.org/&#34;&gt;Jdbi&lt;/a&gt; is also easy by returning 
the values as a &lt;code&gt;ResultIterable&lt;/code&gt;. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;But what if we want to group&amp;#x2F;reduce the data? Can we use the row reducer from Jdbi?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;No. The row reducer will read all data rows and store the result in a map. This may consume a 
ton of memory and may result in killing our application by running out of memory. So no more 
streaming when using a row reducer.&lt;/p&gt;
&lt;h2 id=&#34;Solution&#34;&gt;&lt;a href=&#34;#Solution&#34; class=&#34;headerlink&#34; title=&#34;Solution&#34;&gt;&lt;/a&gt;Solution&lt;/h2&gt;&lt;p&gt;We should&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;get a &lt;code&gt;Stream&lt;/code&gt; instance from the &lt;code&gt;ResultIterable&lt;/code&gt; object (returned from the SQL Object interface)&lt;/li&gt;
&lt;li&gt;not call any terminal operation method on the stream which would result in reading all the data and thus again ending streaming the data&lt;/li&gt;
&lt;li&gt;prepare the order of the data so that we can identify when a group level break&amp;#x2F;control break occurs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We cannot use the annotation &lt;code&gt;UseRowReducer&lt;/code&gt; from Jdbi for this as this would result in reading all
the data and terminates streaming. We need to do the row reducing by ourselves.&lt;/p&gt;
&lt;h2 id=&#34;Row-Reducer&#34;&gt;&lt;a href=&#34;#Row-Reducer&#34; class=&#34;headerlink&#34; title=&#34;Row Reducer&#34;&gt;&lt;/a&gt;Row Reducer&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;But what exactly is row reducing? &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It is exactly what is says: &lt;em&gt;reducing rows&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Most of the time we have some “master” data row which gets some extra columns from some “detail” data,
f. e. an order (master) with many items (details).&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;M1a  |  M1b  |  M1c  |  D1a  | D1b&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;M1a  |  M1b  |  M1c  |  D2a  | D2b&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;M1a  |  M1b  |  M1c  |  D3a  | D3b&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;M1a  |  M1b  |  M1c  |  D4a  | D4b&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;M2a  |  M2b  |  M3c  |  D8a  | D8b&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;M2a  |  M2b  |  M3c  |  D9a  | D9b&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Here we have two master data entries where one master data entry (M1) has four detail entries and the
other (M2) has two detail entries.&lt;/p&gt;
&lt;p&gt;For M1 we need to reduce it to one object which has a list with 4 child objects. So when the key of
the master data changes we need to make a “break”, process the master data and its details data and 
then start with the next master data entry.&lt;/p&gt;
&lt;p&gt;This can be done with implementing the Jbdi &lt;code&gt;RowMapper&lt;/code&gt; interface.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;CustomerControlBreakMapper&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;RowMapper&lt;/span&gt;&amp;lt;Customer&amp;gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; Customer customer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; Customer returnedCustomer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; RowMapper&amp;lt;Customer&amp;gt; customerMapper = BeanMapper.of(Customer.class, &lt;span class=&#34;string&#34;&gt;&amp;quot;c&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; RowMapper&amp;lt;Email&amp;gt; emailMapper = BeanMapper.of(Email.class, &lt;span class=&#34;string&#34;&gt;&amp;quot;e&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; RowMapper&amp;lt;EmailType&amp;gt; emailTypeMapper = BeanMapper.of(EmailType.class, &lt;span class=&#34;string&#34;&gt;&amp;quot;et&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;meta&#34;&gt;@Override&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Customer &lt;span class=&#34;title function_&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;(ResultSet rs, StatementContext ctx)&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;throws&lt;/span&gt; SQLException &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (customer == &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt; || !customer.getId().equals(rs.getInt(&lt;span class=&#34;string&#34;&gt;&amp;quot;c_id&amp;quot;&lt;/span&gt;))) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            returnedCustomer = customer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            customer = customerMapper.map(rs, ctx);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125; &lt;span class=&#34;keyword&#34;&gt;else&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            returnedCustomer = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;type&#34;&gt;Email&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;email&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; emailMapper.map(rs, ctx);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        email.setEmailType(emailTypeMapper.map(rs, ctx));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        customer.addEmail(email);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; returnedCustomer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;With this implementation we get a &lt;code&gt;Customer&lt;/code&gt; object with its e-mail addresses returned on each 
control break. On any other row we just get &lt;code&gt;null&lt;/code&gt; (which we can later filter out with the 
&lt;code&gt;java.util.stream.Stream::filter&lt;/code&gt; method). So in the end we just get our customer objects 
(with the e-mail address objects) in the stream.&lt;/p&gt;
&lt;h2 id=&#34;Putting-it-all-together&#34;&gt;&lt;a href=&#34;#Putting-it-all-together&#34; class=&#34;headerlink&#34; title=&#34;Putting it all together&#34;&gt;&lt;/a&gt;Putting it all together&lt;/h2&gt;&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Pair&amp;lt;Stream&amp;lt;Customer&amp;gt;, Handle&amp;gt; &lt;span class=&#34;title function_&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;()&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// get the database connection&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;type&#34;&gt;Handle&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;handle&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; jdbi.open();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;type&#34;&gt;CustomerControlBreakMapper&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;mapper&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;CustomerControlBreakMapper&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    handle.registerRowMapper(mapper);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// use SQLObjects for executing the SQL&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;type&#34;&gt;CustomerDao&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;dao&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; handle.attach(CustomerDao.class);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// get the stream from the ResultIterable&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Stream&amp;lt;Customer&amp;gt; customerStream = dao.list().stream();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Pair.of(stream, handle);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;That looks pretty manageable but …&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hmmm … the last group of data is missing in the result!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As with any control break logic we need to adjust the code a bit because there is no control break
after the last group of data rows.&lt;/p&gt;
&lt;h2 id=&#34;The-Final-Group&#34;&gt;&lt;a href=&#34;#The-Final-Group&#34; class=&#34;headerlink&#34; title=&#34;The Final Group&#34;&gt;&lt;/a&gt;The Final Group&lt;/h2&gt;&lt;p&gt;The code is almost complete as it is because we already have processed every data row and have the
last customer object with all the e-mail addresses. It just isn’t returned as there is no control
break after the last row.&lt;/p&gt;
&lt;p&gt;The variable &lt;code&gt;customer&lt;/code&gt; in the &lt;code&gt;CustomerControlBreakMapper&lt;/code&gt; class already holds the final customer 
object ready to be returned. We just need to get it somehow.&lt;/p&gt;
&lt;p&gt;For that we introduce a new interface:&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;import&lt;/span&gt; java.util.Objects;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;import&lt;/span&gt; java.util.stream.Stream;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;ControlBreakSupport&lt;/span&gt;&amp;lt;T&amp;gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    T &lt;span class=&#34;title function_&#34;&gt;getFinalGroup&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;()&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;default&lt;/span&gt; Stream&amp;lt;T&amp;gt; &lt;span class=&#34;title function_&#34;&gt;stream&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;()&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Stream.generate(&lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;::getFinalGroup).limit(&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;).filter(Objects::nonNull);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;This interface lets you create a new stream of the final group (customer) object which we can
concat with the &lt;code&gt;Stream&lt;/code&gt; instance we get from the &lt;code&gt;ResultIterable&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let’s do it!&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;CustomerControlBreakMapper&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;RowMapper&lt;/span&gt;&amp;lt;Customer&amp;gt;, ControlBreakSupport&amp;lt;Customer&amp;gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; Customer customer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; Customer returnedCustomer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; RowMapper&amp;lt;Customer&amp;gt; customerMapper = BeanMapper.of(Customer.class, &lt;span class=&#34;string&#34;&gt;&amp;quot;c&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; RowMapper&amp;lt;Email&amp;gt; emailMapper = BeanMapper.of(Email.class, &lt;span class=&#34;string&#34;&gt;&amp;quot;e&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; RowMapper&amp;lt;EmailType&amp;gt; emailTypeMapper = BeanMapper.of(EmailType.class, &lt;span class=&#34;string&#34;&gt;&amp;quot;et&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;meta&#34;&gt;@Override&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Customer &lt;span class=&#34;title function_&#34;&gt;getFinalGroup&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;()&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; customer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;meta&#34;&gt;@Override&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Customer &lt;span class=&#34;title function_&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;(ResultSet rs, StatementContext ctx)&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;throws&lt;/span&gt; SQLException &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (customer == &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt; || !customer.getId().equals(rs.getInt(&lt;span class=&#34;string&#34;&gt;&amp;quot;c_id&amp;quot;&lt;/span&gt;))) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            returnedCustomer = customer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            customer = customerMapper.map(rs, ctx);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125; &lt;span class=&#34;keyword&#34;&gt;else&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            returnedCustomer = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;type&#34;&gt;Email&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;email&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; emailMapper.map(rs, ctx);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        email.setEmailType(emailTypeMapper.map(rs, ctx));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        customer.addEmail(email);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; returnedCustomer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Not much has changed but now we can concat the two streams and will get all the customer objects
from the resulting stream.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Pair&amp;lt;Stream&amp;lt;Customer&amp;gt;, Handle&amp;gt; &lt;span class=&#34;title function_&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;()&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// get the database connection&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;type&#34;&gt;Handle&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;handle&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; jdbi.open();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;type&#34;&gt;CustomerControlBreakMapper&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;mapper&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;CustomerControlBreakMapper&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    handle.registerRowMapper(mapper);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// using SQLObjects for executing the SQL&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;type&#34;&gt;CustomerDao&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;dao&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; handle.attach(CustomerDao.class);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// get the stream from the ResultIterable&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Stream&amp;lt;Customer&amp;gt; customerStream = dao.list().stream();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// get the stream with the final customer&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Stream&amp;lt;Customer&amp;gt; finalGroupStream = mapper.stream();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// concat those two streams to one stream&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Stream&amp;lt;Customer&amp;gt; stream = Stream.concat(customerStream, finalGroupStream).filter(Objects::nonNull);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Pair.of(stream, handle);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;Wrap-Up&#34;&gt;&lt;a href=&#34;#Wrap-Up&#34; class=&#34;headerlink&#34; title=&#34;Wrap Up&#34;&gt;&lt;/a&gt;Wrap Up&lt;/h2&gt;&lt;p&gt;Streaming doesn’t exclude grouping if done right. And the result is pretty manageable with 
small classes and interfaces.&lt;/p&gt;
&lt;p&gt;If you found another solution to the grouping &amp;#x2F; row reducing problem I would be really
interested in hearing it.&lt;/p&gt;
&lt;p&gt;Happy streaming!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2025/01/13/2025-01-13-streaming-client/</guid>
            <title>Streaming data ... the client side</title>
            <link>http://example.com/2025/01/13/2025-01-13-streaming-client/</link>
            <category>Java</category>
            <pubDate>Mon, 13 Jan 2025 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;Here comes the counterpart to the “Jdbi and Streaming” post. This post will cover the client
side of things. We will use Jackson’s &lt;code&gt;JsonParser&lt;/code&gt; and &lt;code&gt;ObjectMapper&lt;/code&gt; to process the streamed 
data chunk by chunk … or rather JSON object by JSON object :)&lt;/p&gt;
&lt;p&gt;This implementation doesn’t rely on any specific web framework as it just operates on an 
&lt;code&gt;InputStream&lt;/code&gt;. And as it uses just an InputStream it doesn’t even necessarily has to be 
the response of a web server but could also be a &lt;code&gt;FileInputStream&lt;/code&gt; for a file on disk.&lt;/p&gt;
&lt;h2 id=&#34;Code&#34;&gt;&lt;a href=&#34;#Code&#34; class=&#34;headerlink&#34; title=&#34;Code&#34;&gt;&lt;/a&gt;Code&lt;/h2&gt;&lt;p&gt;Most of the code is from the StackOverflow question 
&lt;a href=&#34;https://stackoverflow.com/questions/6511880/how-to-parse-a-json-input-stream&#34;&gt;“How to parse a JSON input stream?”&lt;/a&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;87&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;88&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;89&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;90&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;91&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;92&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;93&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;94&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;95&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;96&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;97&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;98&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;99&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;100&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;101&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;102&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;103&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;104&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;105&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;106&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;107&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;108&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;109&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;110&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;111&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;112&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;113&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;114&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;115&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;116&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;117&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;118&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;119&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;120&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;121&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;122&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;123&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;JsonObjectIterator&lt;/span&gt;&amp;lt;T&amp;gt; &lt;span class=&#34;keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;Iterator&lt;/span&gt;&amp;lt;T&amp;gt;, Closeable &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;final&lt;/span&gt; InputStream inputStream;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; JsonParser jsonParser;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;type&#34;&gt;boolean&lt;/span&gt; isInitialized;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; T nextObject;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; ObjectMapper objectMapper;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; Class&amp;lt;T&amp;gt; clazz;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title function_&#34;&gt;JsonObjectIterator&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;(&lt;span class=&#34;keyword&#34;&gt;final&lt;/span&gt; InputStream inputStream, Class&amp;lt;T&amp;gt; clazz)&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;(inputStream, clazz, &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;ObjectMapper&lt;/span&gt;());&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title function_&#34;&gt;JsonObjectIterator&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;(&lt;span class=&#34;keyword&#34;&gt;final&lt;/span&gt; InputStream inputStream, Class&amp;lt;T&amp;gt; clazz, ObjectMapper objectMapper)&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.inputStream = inputStream;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.isInitialized = &lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.nextObject = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.objectMapper = objectMapper;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.clazz = clazz;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title function_&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;()&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.initJsonParser();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.initFirstElement();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.isInitialized = &lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title function_&#34;&gt;initJsonParser&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;()&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;type&#34;&gt;JsonFactory&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;jsonFactory&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; objectMapper.getFactory();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.jsonParser = jsonFactory.createParser(inputStream);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125; &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;final&lt;/span&gt; IOException e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;RuntimeException&lt;/span&gt;(&lt;span class=&#34;string&#34;&gt;&amp;quot;There was a problem setting up the JsonParser: &amp;quot;&lt;/span&gt; + e.getMessage(), e);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title function_&#34;&gt;initFirstElement&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;()&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;comment&#34;&gt;// Check that the first element is the start of an array&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;type&#34;&gt;JsonToken&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;arrayStartToken&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.jsonParser.nextToken();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (arrayStartToken != JsonToken.START_ARRAY) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;IllegalStateException&lt;/span&gt;(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        &lt;span class=&#34;string&#34;&gt;&amp;quot;The first element of the Json structure was expected to be a start array token, but it was: &amp;quot;&lt;/span&gt; + arrayStartToken);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;comment&#34;&gt;// Initialize the first object&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.initNextObject();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125; &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;final&lt;/span&gt; Exception e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;RuntimeException&lt;/span&gt;(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &lt;span class=&#34;string&#34;&gt;&amp;quot;There was a problem initializing the first element of the Json Structure: &amp;quot;&lt;/span&gt; + e.getMessage(), e);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title function_&#34;&gt;initNextObject&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;()&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;type&#34;&gt;JsonToken&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;nextToken&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.jsonParser.nextToken();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;comment&#34;&gt;// Check for the end of the array which will mean we&amp;#x27;re done&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (nextToken == JsonToken.END_ARRAY) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.nextObject = &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;comment&#34;&gt;// Make sure the next token is the start of an object&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (nextToken != JsonToken.START_OBJECT) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;IllegalStateException&lt;/span&gt;(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        &lt;span class=&#34;string&#34;&gt;&amp;quot;The next token of Json structure was expected to be a start object token, but it was: &amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                                + nextToken);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;comment&#34;&gt;// Get the next object and make sure it&amp;#x27;s not null&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.nextObject = &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.jsonParser.readValueAs(clazz);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (&lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.nextObject == &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;IllegalStateException&lt;/span&gt;(&lt;span class=&#34;string&#34;&gt;&amp;quot;The next parsed object of the Json structure was null&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125; &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (&lt;span class=&#34;keyword&#34;&gt;final&lt;/span&gt; Exception e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;RuntimeException&lt;/span&gt;(&lt;span class=&#34;string&#34;&gt;&amp;quot;There was a problem initializing the next Object: &amp;quot;&lt;/span&gt; + e.getMessage(), e);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;meta&#34;&gt;@Override&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;type&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;title function_&#34;&gt;hasNext&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;()&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (!&lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.isInitialized) &amp;#123; &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.init(); &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.nextObject != &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;meta&#34;&gt;@Override&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; T &lt;span class=&#34;title function_&#34;&gt;next&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;()&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// This method will return the current object and initialize the next object so&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// hasNext will always have knowledge of the current state&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// Makes sure we&amp;#x27;re initialized first&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (!&lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.isInitialized) &amp;#123; &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.init(); &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// Store the current next object for return&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;type&#34;&gt;T&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;currentNextObject&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.nextObject;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// Initialize the next object&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.initNextObject();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; currentNextObject;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;meta&#34;&gt;@Override&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title function_&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;throws&lt;/span&gt; IOException &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.jsonParser.close();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125; &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (Exception e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;comment&#34;&gt;// close quietly&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.inputStream.close();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125; &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (Exception e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;comment&#34;&gt;// close quietly&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;So you just need to get the &lt;code&gt;InputStream&lt;/code&gt; (from the HTTP client of your choice) and pass 
it to the &lt;code&gt;JsonObjectIterator&lt;/code&gt;. As we want to iterator over a number of JSON objects 
received from the web service the &lt;code&gt;JsonObjectIterator&lt;/code&gt; expects a JSON array as the top
level entity in the InputStream.&lt;/p&gt;
&lt;p&gt;All in all … no magic to be found here ;-)&lt;/p&gt;
&lt;h3 id=&#34;Wrap-Up&#34;&gt;&lt;a href=&#34;#Wrap-Up&#34; class=&#34;headerlink&#34; title=&#34;Wrap Up&#34;&gt;&lt;/a&gt;Wrap Up&lt;/h3&gt;&lt;p&gt;It is pretty easy to process a big JSON array chunk by chunk with the help of the Jackson
library. I skipped the part on how to use the &lt;code&gt;JsonObjectIterator&lt;/code&gt; as it is just an iterator
and I am sure you already know how to code a &lt;code&gt;while&lt;/code&gt; loop with calling &lt;code&gt;hasNext&lt;/code&gt; and &lt;code&gt;next&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Happy streaming!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2024/12/15/2024-12-15-ilevator-pulsar/</guid>
            <title>ILE languages can also play in the streaming game</title>
            <link>http://example.com/2024/12/15/2024-12-15-ilevator-pulsar/</link>
            <category>RPG</category>
            <category>IBMi</category>
            <category>ILEvator</category>
            <pubDate>Sun, 15 Dec 2024 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;IBM i as a platform was always able to participate in the streaming game by leveraging Java or 
Node.js. But integrating the ILE environment in a process which needs access to a streaming 
platform was not as straight forward as it should be. Many streaming middlewares support access 
through HTTP but this is far from optimal and often not very feature rich.&lt;/p&gt;
&lt;h2 id=&#34;But-what-is-data-streaming&#34;&gt;&lt;a href=&#34;#But-what-is-data-streaming&#34; class=&#34;headerlink&#34; title=&#34;But what is data streaming?&#34;&gt;&lt;/a&gt;But what is data streaming?&lt;/h2&gt;&lt;p&gt;&lt;a href=&#34;https://www.techtarget.com/searchnetworking/definition/data-streaming&#34;&gt;Data streaming&lt;/a&gt; is about 
processing data in (near) real-time as it arrives. It focuses on the continuous flow of data, 
high throughput and short processing times. &lt;/p&gt;
&lt;p&gt;Well-known open source frameworks for data streaming are&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://kafka.apache.org/&#34;&gt;Apache Kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://pulsar.apache.org/&#34;&gt;Apache Pulsar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://spark.apache.org/&#34;&gt;Apache Spark&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Many cloud platform providers offer their own flavour of a commercial data streaming service.&lt;/p&gt;
&lt;h2 id=&#34;How-to-join-the-fray&#34;&gt;&lt;a href=&#34;#How-to-join-the-fray&#34; class=&#34;headerlink&#34; title=&#34;How to join the fray?&#34;&gt;&lt;/a&gt;How to join the fray?&lt;/h2&gt;&lt;p&gt;The &lt;a href=&#34;https://www.ibm.com/docs/en/i/7.5?topic=languages-ile-concepts&#34;&gt;ILE environment&lt;/a&gt; has often
been neglected by IBM. There is no easy and straight forward way to send&amp;#x2F;receive data to&amp;#x2F;from a 
streaming platform using ILE languages. Many of these platforms ship their own client library for 
various languages (but of course not for any ILE language or at least not usable in the ILE 
environment as some ship C&amp;#x2F;C++ versions but not compatible with the outdated ILE C&amp;#x2F;C++ compilers).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Is there no common nominator for a protocol to use?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In the message queueing and processing area there are some protocols which are widely used
and implemented by almost every message queueing system. &lt;a href=&#34;https://mqtt.org/&#34;&gt;MQTT&lt;/a&gt;, 
&lt;a href=&#34;https://www.amqp.org/&#34;&gt;AMQP&lt;/a&gt;, &lt;a href=&#34;https://stomp.github.io/&#34;&gt;STOMP&lt;/a&gt; to name a few.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What about data streaming?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are some streaming protocols like RTMP, WebRTC, FTL, HLS or SRT but these have a very specific
use case (mostly video and audio streaming).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;So is there really no way of streaming data with a common protocol?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If we leave the specific protocols and look at more generic solutions there is a protocol which is 
not an obvious candidate but should be looked at more closely: &lt;a href=&#34;https://websocket.org/&#34;&gt;WebSocket&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In a nutshell, WebSocket is a technology that enables bidirectional, full-duplex communication 
between client and server over a persistent, single-socket connection. The intent is to provide
what is essentially an as-close-to-raw-as-possible TCP communication layer to web application 
developers while adding a few abstractions to eliminate certain friction that would otherwise 
exist concerning the way the web works. A WebSocket connection starts as an HTTP request&amp;#x2F;response 
handshake; beyond this handshake, WebSocket and HTTP are fundamentally different. — websocket.org&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What makes this protocol interesting is the fact that it is not too specific about the use cases.
And as its requirement is just a socket connection it can be used in any environment which supports
sockets, including server to server use cases and not only the web&amp;#x2F;browser.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;One step closer … but where is the implementation?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/sitemule/ILEvator&#34;&gt;Here!&lt;/a&gt; :)&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://github.com/sitemule/ILEvator&#34;&gt;ILEvator&lt;/a&gt; project supports the WebSocket protocol with
its latest contribution. The &lt;a href=&#34;https://sitemule.github.io/ilevator/websocket.html&#34;&gt;user guide&lt;/a&gt; shows
how to initiate a WebSocket connection and how to send and receive data. It has been tested with 
the reference implementation for the Jakarta WebSocket specification, 
&lt;a href=&#34;https://eclipse-ee4j.github.io/tyrus/&#34;&gt;Eclipse Tyrus&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;ILEvator also works with Apache Pulsar and the Node.js WebSocket server from the 
&lt;a href=&#34;https://github.com/websockets/ws&#34;&gt;ws&lt;/a&gt; library. Apache Kafka does not support WebSocket by itself
but there are some open source tools which utilize the Node.js &lt;code&gt;ws&lt;/code&gt; library and thus can be used
with ILEvator. Also some cloud platforms support WebSocket by providing their own WebSocket service
to their Kafka cluster.&lt;/p&gt;
&lt;h2 id=&#34;Wrap-Up&#34;&gt;&lt;a href=&#34;#Wrap-Up&#34; class=&#34;headerlink&#34; title=&#34;Wrap Up&#34;&gt;&lt;/a&gt;Wrap Up&lt;/h2&gt;&lt;p&gt;All it took was about &lt;a href=&#34;https://github.com/sitemule/ILEvator/blob/main/src/websocket.rpgmod#L819&#34;&gt;800 lines of code&lt;/a&gt; 
to let you join the streaming game. It is sad to see that there is not more effort to integrate 
the ILE environment in the computing environments of today.&lt;/p&gt;
&lt;p&gt;Happy streaming!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2024/12/12/2024-12-12-jdbi-streaming/</guid>
            <title>Jdbi and Streaming</title>
            <link>http://example.com/2024/12/12/2024-12-12-jdbi-streaming/</link>
            <category>Java</category>
            <pubDate>Thu, 12 Dec 2024 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;About half a year has this post been in my draft stash. Finally I found some time and 
motivation to finish it. And here it is! :)&lt;/p&gt;
&lt;p&gt;The streaming of data block by block from your web service can have significant advantages in 
comparision to collecting all the data on the server side and then sending it to the client.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;less memory consumption&lt;/li&gt;
&lt;li&gt;faster processing time&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;Less-Memory-Consumption&#34;&gt;&lt;a href=&#34;#Less-Memory-Consumption&#34; class=&#34;headerlink&#34; title=&#34;Less Memory Consumption&#34;&gt;&lt;/a&gt;Less Memory Consumption&lt;/h2&gt;&lt;p&gt;In most cases when it comes to web services the data will be collected on the server and then sent
to the client. And this is totally ok and works pretty well as in most cases the data transfered 
from and to web services is not so big.&lt;/p&gt;
&lt;p&gt;But when it comes to transfering several hundreds of MB in one request it may not be so efficient
and may even kill the server because of consuming all the available memory … ever seen the 
OutOfMemoryError? ;-)&lt;/p&gt;
&lt;p&gt;But what if you just process one entity at a time and send that chunk of data to the client? Memory
consumption wouldn’t be any problem at all.&lt;/p&gt;
&lt;p&gt;Having a solution for the server is great. But you also have to take care of the client side (if it
is in your hands) so that you don’t just push the memory consumption problem from the server to the
client. You need to process the data on the client also block by block.&lt;/p&gt;
&lt;h2 id=&#34;Faster-Processing-Time&#34;&gt;&lt;a href=&#34;#Faster-Processing-Time&#34; class=&#34;headerlink&#34; title=&#34;Faster Processing Time&#34;&gt;&lt;/a&gt;Faster Processing Time&lt;/h2&gt;&lt;p&gt;When streaming the data from the server to the client without having to first collect the whole data
on the server side the client gets the first block of data much faster and thus can process the data
earlier. Thus the whole processing time is shorter.&lt;/p&gt;
&lt;h2 id=&#34;Jakarta-JAX-RS-Streaming-Support&#34;&gt;&lt;a href=&#34;#Jakarta-JAX-RS-Streaming-Support&#34; class=&#34;headerlink&#34; title=&#34;Jakarta JAX-RS Streaming Support&#34;&gt;&lt;/a&gt;Jakarta JAX-RS Streaming Support&lt;/h2&gt;&lt;p&gt;JAX-RS supports the streaming of data via the interface &lt;code&gt;jakarta.ws.rs.core.StreamingOutput&lt;/code&gt; which 
can be returned as the response entity.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;return Response.ok(myStreamingOutput).build();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;Jdbi-Streaming-Support&#34;&gt;&lt;a href=&#34;#Jdbi-Streaming-Support&#34; class=&#34;headerlink&#34; title=&#34;Jdbi Streaming Support&#34;&gt;&lt;/a&gt;Jdbi Streaming Support&lt;/h2&gt;&lt;p&gt;Jdbi supports the streaming of data directly from an SQL query out-of-the-box in the Core API and 
also in the SQL Objects module with the interface &lt;code&gt;org.jdbi.v3.core.result.ResultIterable&lt;/code&gt;. This 
interface can be used instead as a return type just like &lt;code&gt;java.util.List&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The interface &lt;code&gt;ResultIterable&lt;/code&gt; comes with multiple methods to work with the data. Just keep in mind
that almost all these methods work similar as in &lt;code&gt;java.util.stream.Stream&lt;/code&gt; as they are almost all
terminal operations. With executing these terminal operations you will load and process all data 
from your query and cannot process the data again with the same &lt;code&gt;ResultIterable&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;From the Jdbi docs:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Almost all operations on the ResultIterable interface are terminal operations. When they finish, 
they close and release all resources, especially the ResultSet and PreparedStatement objects.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;DAO&#34;&gt;&lt;a href=&#34;#DAO&#34; class=&#34;headerlink&#34; title=&#34;DAO&#34;&gt;&lt;/a&gt;DAO&lt;/h3&gt;&lt;p&gt;The DAO interface (using Jdbi SQL Objects) just returns a &lt;code&gt;ResultIterable&lt;/code&gt; instead of a list.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;@SqlQuery(&amp;quot;SELECT ... FROM ... WHERE ...&amp;quot;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ResultIterable&amp;lt;Location&amp;gt; iterate();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h3 id=&#34;Data-Service&#34;&gt;&lt;a href=&#34;#Data-Service&#34; class=&#34;headerlink&#34; title=&#34;Data Service&#34;&gt;&lt;/a&gt;Data Service&lt;/h3&gt;&lt;p&gt;You can use the method &lt;code&gt;stream()&lt;/code&gt; to get a &lt;code&gt;java.util.stream.Stream&lt;/code&gt; object from the 
&lt;code&gt;ResultIterable&lt;/code&gt;. After processing the data from the stream all the allocated resources should
be freed but the Handle instance is still open. You need to pass the handle to the 
&lt;code&gt;StreamingOutput&lt;/code&gt; instance so that you can call &lt;code&gt;close()&lt;/code&gt; on the Handle instance after the
processing. One way of returning both the stream and the handle is by using the &lt;code&gt;Pair&lt;/code&gt; interface
of the Apache Commons Lang project.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;Pair.of(resultIterable.stream(), handle);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;All this can be encapsulated into a service class which is then used by the web service resource
class.&lt;/p&gt;
&lt;h2 id=&#34;JAX-RS-Jdbi&#34;&gt;&lt;a href=&#34;#JAX-RS-Jdbi&#34; class=&#34;headerlink&#34; title=&#34;JAX-RS + Jdbi&#34;&gt;&lt;/a&gt;JAX-RS + Jdbi&lt;/h2&gt;&lt;p&gt;Both parts (JAX-RS and Jdbi) support the streaming of data. Now we just have to bring those two
together. We need to implement &lt;code&gt;StreamingOutput&lt;/code&gt; for this and have to have access to the stream
and the Jdbi Handle object.&lt;/p&gt;
&lt;p&gt;This implementation will output a stream of objects to the client. The objects are deserialized
by using an &lt;code&gt;ObjectMapper&lt;/code&gt; instance from the &lt;a href=&#34;https://github.com/FasterXML/jackson-databind&#34;&gt;Jackson Data Bind&lt;/a&gt;
library. It also lets you limit the number of objects you want to return to the client by passing
the &lt;code&gt;size&lt;/code&gt; parameter to the constructor.&lt;/p&gt;
&lt;p&gt;The stream and the Jdbi handle will be passed to the implementation by using the &lt;code&gt;Pair&lt;/code&gt; interface 
from the Apache Commons Lang project.&lt;/p&gt;
&lt;p&gt;The Jdbi handle will be closed after processing all the objects in the stream.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;JdbiStreamingOutput&lt;/span&gt;&amp;lt;T&amp;gt; &lt;span class=&#34;keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;StreamingOutput&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; ObjectMapper mapper;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; Pair&amp;lt;Stream&amp;lt;T&amp;gt;, Handle&amp;gt; streamHandle;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; Long size;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title function_&#34;&gt;JdbiStreamingOutput&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;(Pair&amp;lt;Stream&amp;lt;T&amp;gt;, Handle&amp;gt; streamHandle, ObjectMapper mapper)&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.streamHandle = streamHandle;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.mapper = mapper;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;title function_&#34;&gt;JdbiStreamingOutput&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;(Pair&amp;lt;Stream&amp;lt;T&amp;gt;, Handle&amp;gt; streamHandle, ObjectMapper mapper, Long size)&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.streamHandle = streamHandle;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.mapper = mapper;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;built_in&#34;&gt;this&lt;/span&gt;.size = (size != &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt; &amp;amp;&amp;amp; size &amp;lt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;) ? &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt; : size;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;meta&#34;&gt;@Override&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;title function_&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;(OutputStream out)&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;throws&lt;/span&gt; IOException, WebApplicationException &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;type&#34;&gt;AtomicInteger&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;AtomicInteger&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;type&#34;&gt;byte&lt;/span&gt;[] comma = &lt;span class=&#34;string&#34;&gt;&amp;quot;,&amp;quot;&lt;/span&gt;.getBytes(StandardCharsets.UTF_8);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        out.write(&lt;span class=&#34;string&#34;&gt;&amp;quot;[&amp;quot;&lt;/span&gt;.getBytes(StandardCharsets.UTF_8));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Stream&amp;lt;T&amp;gt; stream = streamHandle.getLeft().sequential();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (size != &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;) stream = stream.limit(size);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            stream.forEach(c -&amp;gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (i.get() &amp;gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;) out.write(comma);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    mapper.writeValue(out, c);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    i.incrementAndGet();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125; &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (Exception e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;RuntimeException&lt;/span&gt;(&lt;span class=&#34;string&#34;&gt;&amp;quot;Object deserialization failed.&amp;quot;&lt;/span&gt;, e);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125; &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (Exception e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;RuntimeException&lt;/span&gt;(&lt;span class=&#34;string&#34;&gt;&amp;quot;Error on streaming data at element &amp;quot;&lt;/span&gt; + (i.get()+&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;), e);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125; &lt;span class=&#34;keyword&#34;&gt;finally&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (!streamHandle.getRight().isClosed()) streamHandle.getRight().close();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        out.write(&lt;span class=&#34;string&#34;&gt;&amp;quot;]&amp;quot;&lt;/span&gt;.getBytes(StandardCharsets.UTF_8));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        out.flush();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;Web-Service-Resource&#34;&gt;&lt;a href=&#34;#Web-Service-Resource&#34; class=&#34;headerlink&#34; title=&#34;Web Service Resource&#34;&gt;&lt;/a&gt;Web Service Resource&lt;/h2&gt;&lt;p&gt;In the web service resource class a &lt;code&gt;JdbiStreamingOutput&lt;/code&gt; instance is returned as the resposne entity
… and that’s it :).&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; Response &lt;span class=&#34;title function_&#34;&gt;stream&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;()&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Pair&amp;lt;Stream&amp;lt;Location&amp;gt;, Handle&amp;gt; result = locationService.stream();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; Response.ok(&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;JdbiStreamingOutput&lt;/span&gt;&amp;lt;Location&amp;gt;(result, mapper)).build();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;An instance of &lt;code&gt;ObjectMapper&lt;/code&gt; is passed for deserialization to the constructor.&lt;/p&gt;
&lt;h2 id=&#34;Final-Thoughts-and-Hints&#34;&gt;&lt;a href=&#34;#Final-Thoughts-and-Hints&#34; class=&#34;headerlink&#34; title=&#34;Final Thoughts and Hints&#34;&gt;&lt;/a&gt;Final Thoughts and Hints&lt;/h2&gt;&lt;p&gt;Of course there are some limitation to this simple implementation. F. e. you can only 
return an array of objects (which is mostly the case when you stream JSON data ;-) ).&lt;/p&gt;
&lt;p&gt;It is also tied to the libraries Jdbi and Jackson Data Bind. But this is intended and 
pretty easy to abstract away if needed.&lt;/p&gt;
&lt;h3 id=&#34;Hint&#34;&gt;&lt;a href=&#34;#Hint&#34; class=&#34;headerlink&#34; title=&#34;Hint&#34;&gt;&lt;/a&gt;Hint&lt;/h3&gt;&lt;p&gt;&lt;code&gt;@UseRowReducer&lt;/code&gt; cannot be used in this case in the Jdbi DAO interface as it results in
Jdbi collecting all the data on the server before streaming it and thus … no streaming. 
But there is a solution which will be presented in another post.&lt;/p&gt;
&lt;h3 id=&#34;Wrap-Up&#34;&gt;&lt;a href=&#34;#Wrap-Up&#34; class=&#34;headerlink&#34; title=&#34;Wrap Up&#34;&gt;&lt;/a&gt;Wrap Up&lt;/h3&gt;&lt;p&gt;This post demonstrates how to stream data from the database to the client using JAX-RS and
Jdbi. There will be a follow up post about the other side … the client side.&lt;/p&gt;
&lt;p&gt;Happy streaming!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2024/08/01/2024-08-01-RPG-OIDC-M2M/</guid>
            <title>RPG, OpenID Connect and M2M ... or WTF?!</title>
            <link>http://example.com/2024/08/01/2024-08-01-RPG-OIDC-M2M/</link>
            <category>RPG</category>
            <category>IBMi</category>
            <category>ILEvator</category>
            <pubDate>Thu, 01 Aug 2024 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;Just listing some buzz or tech words in a title is not one of the best ways to convey an 
information to an audience. The target audience might not even realize that the article could
be of interest to them because the title is so hard to grasp and understand. None the less …
here we are :) .&lt;/p&gt;
&lt;p&gt;For some RPG developers their decades old unchanged environment is slowly crumbling and some
tasks require to integrate some new stuff … like web services, access tokens and JWT.&lt;/p&gt;
&lt;p&gt;Some developers will happily yell “Hooray!” while others rather reluctantly engage this challenge.&lt;/p&gt;
&lt;p&gt;One such challenge might be sending some data (f. e. customer master data) to some service provider.&lt;/p&gt;
&lt;p&gt;One de facto industry standard for exchanging data is the usage of web services and access tokens
(for authentication and authorization).&lt;/p&gt;
&lt;p&gt;Many service providers use the OpenID Connect standard, parts of it or at least very similar concepts.&lt;/p&gt;
&lt;h2 id=&#34;OpenID-Connect&#34;&gt;&lt;a href=&#34;#OpenID-Connect&#34; class=&#34;headerlink&#34; title=&#34;OpenID Connect&#34;&gt;&lt;/a&gt;OpenID Connect&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;OpenID Connect (OIDC) is an authentication protocol built on OAuth 2.0 that allows applications
to verify the identity of users and obtain basic profile information. It provides an identity 
layer on top of OAuth 2.0, using JSON Web Tokens (JWT) for securely transmitting user information.
OpenID Connect provides several authentication flows to cover most use cases.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In our case (sending customer master data to a service provider via a web service) we have two programs
(machines) which will communicate with each other. There is no human interaction. This is also 
known as M2M (machine to machine).&lt;/p&gt;
&lt;p&gt;One part of securing the communication is by sending an access token alongside the actual data. The
access token can be requested from an IAM service (Identity and Access Management). Most IAM services
support the OpenID Connect protocol.&lt;/p&gt;
&lt;p&gt;OIDC supports the M2M use case with the &lt;em&gt;Client Credentials&lt;/em&gt; flow. This is a very simple flow where
you pass the credentials form urlencoded to the IAM service and get an access token in return.&lt;/p&gt;
&lt;h2 id=&#34;How-to-do-that-in-RPG&#34;&gt;&lt;a href=&#34;#How-to-do-that-in-RPG&#34; class=&#34;headerlink&#34; title=&#34;How to do that in RPG?&#34;&gt;&lt;/a&gt;How to do that in RPG?&lt;/h2&gt;&lt;p&gt;You can do that in RPG in several ways. IBM i and the SQL services provide everything you may need 
for this challenge but it is not always as straightforward as I would like things to be. The tools
I would choose are &lt;a href=&#34;https://github.com/sitemule/ILEvator&#34;&gt;ILEvator&lt;/a&gt; (HTTP client) and 
&lt;a href=&#34;https://github.com/sitemule/noxDB&#34;&gt;noxDB&lt;/a&gt; (JSON parser and builder).&lt;/p&gt;
&lt;p&gt;First you need to install both packages. There are &lt;a href=&#34;https://sitemule.github.io/ilevator/installation.html&#34;&gt;several&lt;/a&gt;
&lt;a href=&#34;https://github.com/sitemule/noxDB&#34;&gt;documentations&lt;/a&gt; showing how to do this.&lt;/p&gt;
&lt;p&gt;In your code add the copy books.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;/include &amp;#x27;ilevator.rpgle&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;/include &amp;#x27;jsonxml.rpgle&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;The code for getting the access token is encapsulated in its own procedure.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc getAccessToken;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    dcl-pi *n varchar(10000);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        url varchar(10000) const;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        clientId varchar(100) const;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        clientSecret varchar(100) const;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        scope varchar(1000) const options(*nopass);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    end-pi;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    dcl-s ilevator pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    dcl-s token varchar(10000);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    dcl-s headers pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    dcl-ds formData likeds(iv_lvarpuchar_t);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    dcl-s responseData varchar(32766);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    dcl-s json pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;   &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ilevator = iv_newHttpClient();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    // We need to register a certificate store if we want to access resources via HTTPS.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    // A default certificate store can be obtained from the ILEvator project repository.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    iv_setCertificate(ilevator : &amp;#x27;/usr/local/etc/certs/ilevator.kdb&amp;#x27; : &amp;#x27;ilevator&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;   &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    if (%parms() &amp;gt;= 4);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        formData = iv_form_of(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#x27;grant_type&amp;#x27; : &amp;#x27;client_credentials&amp;#x27; :&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#x27;client_id&amp;#x27; : clientId :&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#x27;client_secret&amp;#x27; : clientSecret :&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#x27;scope&amp;#x27; : scope&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    else;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        formData = iv_form_of(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#x27;grant_type&amp;#x27; : &amp;#x27;client_credentials&amp;#x27; :&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#x27;client_id&amp;#x27; : clientId :&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#x27;client_secret&amp;#x27; : clientSecret&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    endif;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;   &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    headers = iv_buildList(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;Accept&amp;#x27; : &amp;#x27;application/json&amp;#x27; :&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;Content-Type&amp;#x27; : &amp;#x27;application/x-www-form-urlencoded&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    responseData = iv_postForm(url : formData : headers);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    json = jx_parseString(responseData);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    // The returned JSON should at least have an attribute &amp;quot;access_token&amp;quot; &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    // according to the OpenID Connect spec which contains the token.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    token = jx_getStr(json : &amp;#x27;access_token&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    return token;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    on-exit;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        iv_form_free(formData);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        iv_freeList(headers);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        iv_free(ilevator);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Is this good code ready to use in production? I think not. But that was not my intention in the
first place. This should just give you some starting points on your journey into the World Wild 
Web ;-) .&lt;/p&gt;
&lt;h2 id=&#34;Access-Token-Expiration&#34;&gt;&lt;a href=&#34;#Access-Token-Expiration&#34; class=&#34;headerlink&#34; title=&#34;Access Token Expiration&#34;&gt;&lt;/a&gt;Access Token Expiration&lt;/h2&gt;&lt;p&gt;If you just want to do a one-shot (getting an access token and using it in just a single web service
call) you don’t have to consider anything regarding the lifetime of the access token. If you want or 
need to use the token in several calls you may find yourself in the situation where the token has expired
(which normally results in a failed web service call with an HTTP status 401 from the called web service).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“expired” … what does that mean?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A JWT access token is generated at a given moment. This moment is saved in the token with the claim
(attribute) &lt;em&gt;iat&lt;/em&gt; (issued at). The value for this claim is a &lt;a href=&#34;https://www.unixtimestamp.com/&#34;&gt;unix timestamp&lt;/a&gt;
(which are the seconds counted from the 1970-01-01 at 00:00 o’clock &lt;a href=&#34;https://www.timeanddate.com/worldclock/timezone/utc&#34;&gt;UTC&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;If nothing else is stated this token is valid for any moment in the future. But most tokens have an
explicit lifetime. This lifetime is expressed in another claim : exp (expiration). The value of this
claim is a unix timestamp at which this token will expire and is no longer valid.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://github.com/sitemule/ILEastic&#34;&gt;ILEastic&lt;/a&gt; project provides a JWT service program which
can be used to check if a token has expired, see service program &lt;code&gt;JWT&lt;/code&gt; procedure &lt;code&gt;jwt_isExpired&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;Usage-of-an-Access-Token&#34;&gt;&lt;a href=&#34;#Usage-of-an-Access-Token&#34; class=&#34;headerlink&#34; title=&#34;Usage of an Access Token&#34;&gt;&lt;/a&gt;Usage of an Access Token&lt;/h2&gt;&lt;p&gt;Most of the time you will need to pass the access token alongside the data. When used with the HTTP 
protocol you will need to pass the access token as a bearer token in the &lt;code&gt;Authorization&lt;/code&gt; header entry.&lt;/p&gt;
&lt;p&gt;This can be achieved very easily in ILEvator.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-s headers pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;headers = iv_buildList(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;authentication&amp;#x27; : &amp;#x27;Bearer &amp;#x27; + accessToken&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;string = iv_get(&amp;#x27;http://localhost&amp;#x27; : IV_MEDIA_TYPE_JSON : headers);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;iv_freeList(headers);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Note that the keyword &lt;code&gt;Bearer&lt;/code&gt; is case sensitive though some software libraries are more lenient than
others and will also accept other lower-&amp;#x2F;uppercase combinations.&lt;/p&gt;
&lt;p&gt;The memory for the header list is allocated dynamically. So you &lt;em&gt;always&lt;/em&gt; need to call &lt;code&gt;iv_freeList&lt;/code&gt; to
free any used memory of the list.&lt;/p&gt;
&lt;h2 id=&#34;Wrap-Up&#34;&gt;&lt;a href=&#34;#Wrap-Up&#34; class=&#34;headerlink&#34; title=&#34;Wrap Up&#34;&gt;&lt;/a&gt;Wrap Up&lt;/h2&gt;&lt;p&gt;If you got the basics figured out and know which tools to use it is pretty simple to get an access
token and use it in your application. There is no magic to it.&lt;/p&gt;
&lt;p&gt;Happy web servicing!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2024/06/22/2024-06-30-ileastic-jwk/</guid>
            <title>ILEastic - JSON Web Key</title>
            <link>http://example.com/2024/06/22/2024-06-30-ileastic-jwk/</link>
            <category>RPG</category>
            <category>IBMi</category>
            <category>ILEastic</category>
            <pubDate>Sat, 22 Jun 2024 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;Securing you web services at a single&amp;#x2F;central point like an API gateway is really a nice thing.
But as long as someone can access the web service without going through that single point the
web service should also do some authentication.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Welcome to JWT and JWK!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;JWT provides an essential part of a standardized way for authenticating a request. JWK is
rather optional but makes it much easier how secrets and keys are provided to the verifier 
of token signatures.&lt;/p&gt;
&lt;p&gt;I won’t go into too much detail about JWT and JWK here. You can find many web sites explaining both,
&lt;a href=&#34;https://jwt.io/introduction&#34;&gt;JWT&lt;/a&gt; and &lt;a href=&#34;https://www.rfc-editor.org/rfc/rfc7517&#34;&gt;JWK&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;JSON-Web-Key&#34;&gt;&lt;a href=&#34;#JSON-Web-Key&#34; class=&#34;headerlink&#34; title=&#34;JSON Web Key&#34;&gt;&lt;/a&gt;JSON Web Key&lt;/h2&gt;&lt;p&gt;The JSON Web Key describes the necessary details on how to verify or encrypt when it comes to JWT.
And as the name suggests the data is provided as a JSON object.&lt;/p&gt;
&lt;p&gt;Attributes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;kid - key id : Identifies the web key&lt;/li&gt;
&lt;li&gt;kty - key type : What type of key is stored in this web key&lt;/li&gt;
&lt;li&gt;alg - algorithm : Algorithm to be used with this key&lt;/li&gt;
&lt;li&gt;use : What this key should be used for&lt;/li&gt;
&lt;li&gt;key_ops : What key operations the provided key is intended to be used with (similar to “use”)&lt;/li&gt;
&lt;li&gt;oct : key , base64url encoded&lt;/li&gt;
&lt;li&gt;x5c : array of public keys, binary (DER) format base64 encoded (not base64url)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The mentioned attributes are not a full list but rather what is supported by this JWK implementation.
No all of these attributes are required for every web key.&lt;/p&gt;
&lt;p&gt;Example for HS256:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;kid&amp;quot;: &amp;quot;123-456-789&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;kty&amp;quot;: &amp;quot;oct&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;alg&amp;quot;: &amp;quot;HS256&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;use&amp;quot;: &amp;quot;sig&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;k&amp;quot;: &amp;quot;MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwQUI=&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Example for RS256:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;kid&amp;quot;: &amp;quot;123-456-789-0&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;kty&amp;quot;: &amp;quot;RSA&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;alg&amp;quot;: &amp;quot;RS256&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;use&amp;quot;: &amp;quot;sig&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;x5c&amp;quot;: [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0CfTwHgz2CoNTxx8KYVmstdsVJrvgKqyiui7fO+VFga1pAAPcUsNtU3aRFRE9FWLId9z0AoEdYV7pOWxfFrqJ/M63MByIFfnVLGdR7k6MKKFaiTPNAv7/qAiWvIDHPIyCCbfRRuJ44LOUFTyn6fp9cnUNpAxNCTLJvhebaPB4zF/QroetOHizGfoRybBJyFZ2Rmuog/KKmdgBQSaPqTksOg+SOjf0BuGvycIaIr3rLeK0WO4o9GRBQCtLK6sURwNNO4F7LBPHVC1rtX0GnEZ7FmpOu0UopvYevxgwwNb2FQ5XY3mW4tiAMZLphJehjizXqcghYwu8sczb7QEz611KQIDAQAB&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;h2 id=&#34;JSON-Web-Key-Set&#34;&gt;&lt;a href=&#34;#JSON-Web-Key-Set&#34; class=&#34;headerlink&#34; title=&#34;JSON Web Key Set&#34;&gt;&lt;/a&gt;JSON Web Key Set&lt;/h2&gt;&lt;p&gt;Multiple JSON Web Keys can be added to a JSON Web Key Set (JWKS) which is just a JSON object with the 
attribute &lt;code&gt;keys&lt;/code&gt;. The value of &lt;code&gt;keys&lt;/code&gt; is a simple JSON array where each JSON Web Key is an element
in that array.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &amp;quot;keys&amp;quot;: [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &amp;quot;kid&amp;quot;: &amp;quot;123-456-789&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &amp;quot;kty&amp;quot;: &amp;quot;oct&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &amp;quot;alg&amp;quot;: &amp;quot;HS256&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &amp;quot;use&amp;quot;: &amp;quot;sig&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &amp;quot;k&amp;quot;: &amp;quot;MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwQUI=&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &amp;quot;kid&amp;quot;: &amp;quot;456-789-abc&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &amp;quot;kty&amp;quot;: &amp;quot;oct&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &amp;quot;alg&amp;quot;: &amp;quot;HS512&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &amp;quot;use&amp;quot;: &amp;quot;sig&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &amp;quot;k&amp;quot;: &amp;quot;MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwQUIxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTBBQg==&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  ]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;h2 id=&#34;Implementation&#34;&gt;&lt;a href=&#34;#Implementation&#34; class=&#34;headerlink&#34; title=&#34;Implementation&#34;&gt;&lt;/a&gt;Implementation&lt;/h2&gt;&lt;p&gt;The JWK implementation in ILEastic supports the key types &lt;code&gt;oct&lt;/code&gt; and &lt;code&gt;RSA&lt;/code&gt;. &lt;code&gt;oct&lt;/code&gt; is used for
HMAC-SHA algorithm and &lt;code&gt;RSA&lt;/code&gt; … is self-explanatory :-) .&lt;/p&gt;
&lt;p&gt;The key type &lt;code&gt;oct&lt;/code&gt; requires also the attribute &lt;code&gt;oct&lt;/code&gt;. When using the key type &lt;code&gt;RSA&lt;/code&gt; the 
implementation expects the public key to be base64 encoded (not in PEM format) in the attribute
&lt;code&gt;x5c&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For token verification either the attribute &lt;code&gt;use&lt;/code&gt; with the value &lt;code&gt;sig&lt;/code&gt; or &lt;code&gt;key_ops&lt;/code&gt; with the 
value &lt;code&gt;verify&lt;/code&gt; needs to be present.&lt;/p&gt;
&lt;h2 id=&#34;Loading-a-JWK&#34;&gt;&lt;a href=&#34;#Loading-a-JWK&#34; class=&#34;headerlink&#34; title=&#34;Loading a JWK&#34;&gt;&lt;/a&gt;Loading a JWK&lt;/h2&gt;&lt;p&gt;If used with ILEastic JWK and JWKS should be saved in a stream file in the IFS. The content of 
the stream file is loaded as is. No character conversion takes places. The content needs to be
in an ASCII compatible format like UTF-8 (CCSID 1208) or LATIN1 (CCSID 1252).&lt;/p&gt;
&lt;p&gt;For loading the JWK we need to add at least the JWT and JWT plugin copy books.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;/include &amp;#x27;jwt.rpginc&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;/include &amp;#x27;jwtplugin.rpginc&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The procedure &lt;code&gt;il_jwt_addVerifyOptionsFromJwks&lt;/code&gt; loads a JWK and JWKS from the stream file and
adds it to the ILEastic JWT configuration.&lt;/p&gt;
&lt;p&gt;Note: By default when loading a JWK with &lt;code&gt;il_jwt_addVerifyOptionsFromJwks&lt;/code&gt; the verify options
      lookup method will be “by kid”. That means the JWT plugin will choose a JWK which matches
      the &lt;code&gt;kid&lt;/code&gt; attribute in the JWT from the client request.&lt;/p&gt;
&lt;h2 id=&#34;Selecting-a-Verify-Option&#34;&gt;&lt;a href=&#34;#Selecting-a-Verify-Option&#34; class=&#34;headerlink&#34; title=&#34;Selecting a Verify Option&#34;&gt;&lt;/a&gt;Selecting a Verify Option&lt;/h2&gt;&lt;p&gt;JWKs are added to the plugin configuration as verify options. When adding a verify option an id
can be passed for this verify option. This id will be used for determining which verify option
to use by comparing it with an attribute from the JWT token.&lt;/p&gt;
&lt;p&gt;The ILEastic JWT plugin must decide which JWK to use on a client request. The plugin offers three 
options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JWT_VERIFY_OPTIONS_LOOKUP_NONE : The first registered verify option will be used on every request.&lt;/li&gt;
&lt;li&gt;JWT_VERIFY_OPTIONS_LOOKUP_ISSUER : The issuer claim in the JWT token payload will be used for 
selecting a matching verify option.&lt;/li&gt;
&lt;li&gt;JWT_VERIFY_OPTIONS_LOOKUP_KID : The &lt;code&gt;kid&lt;/code&gt; attribute of the JWT header will be compared with the 
&lt;code&gt;kid&lt;/code&gt; attribute of the JWKs. The JWK of the matching one will be used for token verification.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When adding verify options by calling &lt;code&gt;il_jwt_addVerifyOptionsFromJwks&lt;/code&gt; the &lt;code&gt;kid&lt;/code&gt; will be 
automatically used as the id.&lt;/p&gt;
&lt;p&gt;Note: &lt;code&gt;JWT_VERIFY_OPTIONS_LOOKUP_ISSUER&lt;/code&gt; cannot be used in combination with &lt;code&gt;il_jwt_addVerifyOptionsFromJwks&lt;/code&gt;
as the comparing of &lt;code&gt;kid&lt;/code&gt; and &lt;code&gt;iss&lt;/code&gt; doesn’t make sense.&lt;/p&gt;
&lt;h2 id=&#34;Wrap-Up&#34;&gt;&lt;a href=&#34;#Wrap-Up&#34; class=&#34;headerlink&#34; title=&#34;Wrap Up&#34;&gt;&lt;/a&gt;Wrap Up&lt;/h2&gt;&lt;p&gt;Industry standards like JWT and JWK have found their way into ILEastic and are well supported. 
If your case is not supported just create an issue on &lt;a href=&#34;https://github.com/sitemule/ILEastic/issues&#34;&gt;GitHub&lt;/a&gt;
and present your case.&lt;/p&gt;
&lt;p&gt;Happy web serving!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2024/06/08/2024-06-09-helidon+hikari+jt400/</guid>
            <title>Helidon + HikariCP + JT400 = IllegalArgumentException</title>
            <link>http://example.com/2024/06/08/2024-06-09-helidon+hikari+jt400/</link>
            <category>Java</category>
            <category>IBM</category>
            <pubDate>Sat, 08 Jun 2024 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;Helidon has extra support for using &lt;a href=&#34;https://github.com/brettwooldridge/HikariCP&#34;&gt;HikariCP&lt;/a&gt;. 
A DataSource instance can be easily created with &lt;a href=&#34;https://microprofile.io/specifications/microprofile-config/&#34;&gt;MicroProfile Config&lt;/a&gt;
 and CDI.&lt;/p&gt;
&lt;figure class=&#34;highlight xml&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;dependency&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;groupId&lt;/span&gt;&amp;gt;&lt;/span&gt;io.helidon.integrations.cdi&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;groupId&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;artifactId&lt;/span&gt;&amp;gt;&lt;/span&gt;helidon-integrations-cdi-datasource-hikaricp&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;artifactId&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;dependency&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;h2 id=&#34;The-Problem&#34;&gt;&lt;a href=&#34;#The-Problem&#34; class=&#34;headerlink&#34; title=&#34;The Problem&#34;&gt;&lt;/a&gt;The Problem&lt;/h2&gt;&lt;p&gt;Helidon adds HikariCP 5.0.1 as a dependency. This version of HikariCP supports setting the 
password as a String on the DataSource. But String as a variable type for a password is more
insecure than a primitive data type like a char array, see StackOverflow question 
&lt;a href=&#34;https://stackoverflow.com/a/8881376&#34;&gt;Why is char[] preferred over String for passwords?&lt;/a&gt;.
Some drivers added support for char array for setting the password and overloaded the method 
&lt;code&gt;setPassword&lt;/code&gt;, f. e. the JTOpen project with the JDBC driver for DB2 on i. In case HikariCP gets 
the &lt;code&gt;setPassword&lt;/code&gt; method with the char array as a parameter that will result in an IllegalArgumentException
as it tries to pass the password as a string to it.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;Failed to set property password on target class com.ibm.as400.access.AS400JDBCDataSource&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    java.lang.IllegalArgumentException: argument type mismatch&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        at java.base/java.lang.reflect.Method.invoke(Unknown Source)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        at com.zaxxer.hikari.util.PropertyElf.setProperty(PropertyElf.java:154)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ...&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;So depending on what of version of &lt;code&gt;setPassword&lt;/code&gt; the Java VM returns first it will result in
an IllegalArgumentException or not.&lt;/p&gt;
&lt;h2 id=&#34;The-Solution&#34;&gt;&lt;a href=&#34;#The-Solution&#34; class=&#34;headerlink&#34; title=&#34;The Solution&#34;&gt;&lt;/a&gt;The Solution&lt;/h2&gt;&lt;p&gt;HikariCP version 5.1.0 supports String and char array as a parameter for &lt;code&gt;setPassword&lt;/code&gt;. To get
Maven choose version 5.1.0 over the managed version of the Helidon BOM artifact you need to add
the HikariCP version 5.1.0 to your POM.&lt;/p&gt;
&lt;p&gt;Now the version 5.1.0 of HikariCP should be nearer to the root of the dependency tree than the 
version managed by Helidon and thus should be selected by Maven.&lt;/p&gt;
&lt;p&gt;In Eclipse you can check this by open the &lt;code&gt;pom.xml&lt;/code&gt; in the POM editor and open the tab
&lt;em&gt;Dependency Hierarchy&lt;/em&gt;. It displays the resolved dependencies and their versions.&lt;/p&gt;
&lt;figure class=&#34;highlight xml&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;dependency&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;groupId&lt;/span&gt;&amp;gt;&lt;/span&gt;com.zaxxer&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;groupId&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;artifactId&lt;/span&gt;&amp;gt;&lt;/span&gt;HikariCP&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;artifactId&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;version&lt;/span&gt;&amp;gt;&lt;/span&gt;5.1.0&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;version&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;dependency&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Happy connecting!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2024/05/26/2024-05-26-ileastic-openapi/</guid>
            <title>Tour of Champions - OpenAPI</title>
            <link>http://example.com/2024/05/26/2024-05-26-ileastic-openapi/</link>
            <category>RPG</category>
            <category>IBMi</category>
            <category>ILEastic</category>
            <pubDate>Sun, 26 May 2024 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;blockquote&gt;
&lt;p&gt;What good is a tool if you don’t know how to use it?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;… and the same goes for web services. Without a good documentation on how to use the provided 
web service the value of that web service is lowered massively. In the worst case it is not 
usable at all.&lt;/p&gt;
&lt;p&gt;But instead of writing pages of unstructured prose about the web service the well established
standard &lt;a href=&#34;https://www.openapis.org/&#34;&gt;OpenAPI&lt;/a&gt; should be used as it is very well known in the web 
development world and has lots of tooling support. OpenAPI originated at &lt;a href=&#34;https://swagger.io/&#34;&gt;Swagger&lt;/a&gt;
which offers extra tooling for OpenAPI, f. e. an &lt;a href=&#34;https://editor.swagger.io/&#34;&gt;online OpenAPI editor&lt;/a&gt;
and the &lt;a href=&#34;https://swagger.io/tools/swagger-ui/&#34;&gt;Swagger UI&lt;/a&gt; project.&lt;/p&gt;
&lt;p&gt;The most common format for providing an OpenAPI document is YAML and JSON.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;But hey, YAML and JSON are data exchange formats. I thought we should write a documentation?!?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yes. But 99% of web services today are using HTTP and HTTP has a well defined structure and syntax.
So for many parts of the documentation you just have to enter some keywords. The rest will be 
generated.&lt;/p&gt;
&lt;p&gt;Hint: You can see how your OpenAPI document looks like in web UI with the &lt;a href=&#34;https://editor.swagger.io/&#34;&gt;Swagger OpenAPI editor&lt;/a&gt;. 
It also shows you any syntax errors you might have in your document.&lt;/p&gt;
&lt;h2 id=&#34;Where-to-start&#34;&gt;&lt;a href=&#34;#Where-to-start&#34; class=&#34;headerlink&#34; title=&#34;Where to start?&#34;&gt;&lt;/a&gt;Where to start?&lt;/h2&gt;&lt;p&gt;More or less the OpenAPI document has 3 main parts: common description, paths and components (data model).&lt;/p&gt;
&lt;p&gt;Depending on the tooling you might want to take an existing document and edit that one.
The &lt;a href=&#34;https://www.petstore.dev/&#34;&gt;petstore example&lt;/a&gt; is a good place to start.&lt;/p&gt;
&lt;h2 id=&#34;Ways-to-Integrate&#34;&gt;&lt;a href=&#34;#Ways-to-Integrate&#34; class=&#34;headerlink&#34; title=&#34;Ways to Integrate&#34;&gt;&lt;/a&gt;Ways to Integrate&lt;/h2&gt;&lt;p&gt;You have your OpenAPI document finished. Now what?!&lt;/p&gt;
&lt;p&gt;Again … depending on the tooling you use you may want to provide the OpenAPI document via the 
web service framework (ILEastic in our case). An easy way to do it is to write your own &lt;code&gt;/openapi&lt;/code&gt; 
endpoint &amp;#x2F; route procedure and simply call &lt;code&gt;il_servceStatic&lt;/code&gt;. This will write the content of a 
stream file (your OpenAPI document residing in the IFS) to the stream returned to the client
(f. e. Swagger UI). &lt;/p&gt;
&lt;p&gt;That sounds good but now you need to deploy the program object to the target server and the stream
file. That is doable and most change management systems support that but it is more complicated as
it needs to be.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ILEastic OpenAPI static plugin to the rescue!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;ILEastic provides a plugin for generating and serving the OpenAPI document, 
&lt;a href=&#34;https://github.com/sitemule/ILEastic/tree/master/plugins/openapi-static&#34;&gt;openapi-static&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The tooling provided by this plugin lets you generate an ILE module from the OpenAPI document. This
module has no functions or procedures. It just contains the content of the OpenAPI document.&lt;/p&gt;
&lt;p&gt;Now just add the generated ILE module and two modules from the &lt;code&gt;openapi-static&lt;/code&gt; plugin to your 
ILEastic web service program object and you are done. Everything is now contained inside your
program object and can be easily deployed to any IBM i server just by copying the program object.&lt;/p&gt;
&lt;h2 id=&#34;Embedded-OpenAPI&#34;&gt;&lt;a href=&#34;#Embedded-OpenAPI&#34; class=&#34;headerlink&#34; title=&#34;Embedded OpenAPI&#34;&gt;&lt;/a&gt;Embedded OpenAPI&lt;/h2&gt;&lt;p&gt;Another common way is to embedd OpenAPI snippets into your program code as comments. By doing this
the documentation resides alongside your code and is also managed by any version control system used.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;//&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// @openApiPath&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//  /champion:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//    get:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//      tags:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//        - champion&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//      summary: List IBM champions&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//      description: Returns the details of all awarded IBM champions&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code&gt;openapi-static&lt;/code&gt; plugin provides the command &lt;code&gt;OAPISRCGEN&lt;/code&gt; to parse one or more source stream
files on the IFS and extract the OpenAPI snippets from the source comments to a stream file on the
IFS. From there on it is the same procedure as previously by creating an ILE module from the 
extracted OpenAPI documentation and adding it to the program modules of the program object.&lt;/p&gt;
&lt;p&gt;This whole process is described in the &lt;a href=&#34;https://github.com/sitemule/ILEastic/blob/master/plugins/openapi-static/README.md&#34;&gt;README&lt;/a&gt; of the &lt;code&gt;openapi-static&lt;/code&gt; plugin.&lt;/p&gt;
&lt;h2 id=&#34;Hosting-your-own-UI&#34;&gt;&lt;a href=&#34;#Hosting-your-own-UI&#34; class=&#34;headerlink&#34; title=&#34;Hosting your own UI&#34;&gt;&lt;/a&gt;Hosting your own UI&lt;/h2&gt;&lt;p&gt;Now you got your OpenAPI document(s) served by ILEastic but what now? A very convenient way of working
with your OpenAPIs is to setup your own &lt;a href=&#34;https://github.com/swagger-api/swagger-ui&#34;&gt;Swagger UI&lt;/a&gt; 
instance and configure your various OpenAPI documents there. This can be easily done with an Apache
HTTP server instance on IBM i.&lt;/p&gt;
&lt;h2 id=&#34;Wrap-Up&#34;&gt;&lt;a href=&#34;#Wrap-Up&#34; class=&#34;headerlink&#34; title=&#34;Wrap Up&#34;&gt;&lt;/a&gt;Wrap Up&lt;/h2&gt;&lt;p&gt;ILEastic is a very flexible and extendable framework which can cover every needs the modern developer
may have. If a topic is not covered yet it is often not very hard to implement it yourself.&lt;/p&gt;
&lt;p&gt;Happy OpenAPI serving!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2024/05/19/2024-05-19-ileastic-jwt-refactored/</guid>
            <title>Tour of Champions - JWT</title>
            <link>http://example.com/2024/05/19/2024-05-19-ileastic-jwt-refactored/</link>
            <category>RPG</category>
            <category>IBMi</category>
            <category>ILEastic</category>
            <pubDate>Sun, 19 May 2024 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;For a long time &lt;a href=&#34;https://en.wikipedia.org/wiki/Basic_access_authentication&#34;&gt;Basic Auth&lt;/a&gt; was the 
defacto standard in authentication when it comes to HTTP and the web. But those times are fast 
fading into oblivion as standards like &lt;a href=&#34;https://en.wikipedia.org/wiki/OAuth&#34;&gt;OAuth&lt;/a&gt; and 
&lt;a href=&#34;https://en.wikipedia.org/wiki/JSON_Web_Token&#34;&gt;JWT&lt;/a&gt; have established themselves very well.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/sitemule/ILEastic&#34;&gt;ILEastic&lt;/a&gt; has supported Basic Auth and JWT authentication
for some time now. Recently the JWT plugin has gotten a contribution which has now been adapted 
to the rest of the ILEastic code base. The adaption resulted in an incompatible API to the last 
version but added support for RSA asymmetric algorithm.&lt;/p&gt;
&lt;p&gt;The new JWT service program version supports the following algorithms for verification:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HS256&lt;/li&gt;
&lt;li&gt;HS384&lt;/li&gt;
&lt;li&gt;HS512&lt;/li&gt;
&lt;li&gt;RS256&lt;/li&gt;
&lt;li&gt;RS384&lt;/li&gt;
&lt;li&gt;RS512&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;Example&#34;&gt;&lt;a href=&#34;#Example&#34; class=&#34;headerlink&#34; title=&#34;Example&#34;&gt;&lt;/a&gt;Example&lt;/h2&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;**FREE&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ctl-opt dftactgrp(*no) actgrp(*caller) main(main) bnddir(&amp;#x27;ILEASTIC&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;/include &amp;#x27;ileastic.rpgle&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;/include &amp;#x27;jwt.rpginc&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;/include &amp;#x27;jwtplugin.rpginc&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc main;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-ds config likeds(IL_CONFIG);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-ds jwtOptions likeds(jwt_options_t) inz;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  config.port = 35801;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  config.host = &amp;#x27;*ANY&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  jwtOptions.key = &amp;#x27;my_really_secret_key_which_must_be_at_least_32_chars_long&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  il_jwt_addVerifyOptions(jwtOptions);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  il_addPlugin(config : %paddr(&amp;#x27;il_jwt_filter&amp;#x27;) : IL_PREREQUEST);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  il_addRoute(config : %paddr(sayHello) : IL_GET : &amp;#x27;/hello&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  il_listen (config);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc sayHello;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-pi *n;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    request  likeds(IL_REQUEST);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    response likeds(IL_RESPONSE);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  end-pi;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s name varchar(100);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  name = il_getQueryParameter(request : &amp;#x27;name&amp;#x27; : &amp;#x27;World&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  response.contentType = &amp;#x27;text/plain&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  il_responseWrite(response : &amp;#x27;Hello &amp;#x27; + name);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;h2 id=&#34;Load-Sign-Key-from-Stream-File&#34;&gt;&lt;a href=&#34;#Load-Sign-Key-from-Stream-File&#34; class=&#34;headerlink&#34; title=&#34;Load Sign Key from Stream File&#34;&gt;&lt;/a&gt;Load Sign Key from Stream File&lt;/h2&gt;&lt;p&gt;In the example above the key for verifying the JWT token signature is directly set in the code.
Normally you wouldn’t do this. The key would be loaded from some external source like a file
so that it could be changed without the need to change and recompile the whole program. The JWT
plugin directly supports loading the key from a stream file like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;jwtOptions.key = il_jwt_loadKeyFromStreamFile(&amp;#x27;/path/to/signkey/file&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The content of the file will be read in binary mode. There will be no character conversion. If it
is a sign key for the HS algorithm or a RSA public key (&lt;a href=&#34;https://www.ssl.com/guide/pem-der-crt-and-cer-x-509-encodings-and-conversions/&#34;&gt;PEM&lt;/a&gt; 
format) the content should be in an ASCII compatible character set like 1208 (UTF-8) or 1252 (Latin1).&lt;/p&gt;
&lt;h2 id=&#34;JWT-Algorithm-“none”&#34;&gt;&lt;a href=&#34;#JWT-Algorithm-“none”&#34; class=&#34;headerlink&#34; title=&#34;JWT Algorithm “none”&#34;&gt;&lt;/a&gt;JWT Algorithm “none”&lt;/h2&gt;&lt;p&gt;Besides the listed supported algorithms for JWT tokens it is also worthy to mention that the JWT
algorithm “none” will always be rejected as it is considered insecure.&lt;/p&gt;
&lt;h2 id=&#34;Specific-JWT-Algorithm&#34;&gt;&lt;a href=&#34;#Specific-JWT-Algorithm&#34; class=&#34;headerlink&#34; title=&#34;Specific JWT Algorithm&#34;&gt;&lt;/a&gt;Specific JWT Algorithm&lt;/h2&gt;&lt;p&gt;To make things even more secure you can optionally specify a JWT algorithm in the options. JWT 
tokens with a different algorithm will be rejected.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;jwtOptions.alg = JWT_RS256;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;h2 id=&#34;Multi-Tenant-Support&#34;&gt;&lt;a href=&#34;#Multi-Tenant-Support&#34; class=&#34;headerlink&#34; title=&#34;Multi-Tenant Support&#34;&gt;&lt;/a&gt;Multi-Tenant Support&lt;/h2&gt;&lt;p&gt;Previously only a single key could be configured for JWT verification. Now multiple keys can be
added to the JWT plugin configuration by adding an id on registration.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;jwtOptions.key = il_jwt_loadKeyFromStreamFile(&amp;#x27;/var/local/etc/rpgenxtgen.pub&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;il_jwt_addVerifyOptions(jwtOptions : &amp;#x27;https://rpgnextgen.com&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;jwtOptions.key = il_jwt_loadKeyFromStreamFile(&amp;#x27;/var/local/etc/sitemule.pub&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;il_jwt_addVerifyOptions(jwtOptions : &amp;#x27;https://sitemule.com&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;But how will ILEastic decide which key to use for JWT verification? ILEastic supports the lookup
of the key by the “kid” attribute in the JWT header and by the issuer claim (iss) in the payload.
The value of the id must match with the value of the attribute from the JWT token.&lt;/p&gt;
&lt;p&gt;The method of verify options lookup can be set with the following code:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;il_jwt_setVerifyOptionsLookupMethod(JWT_VERIFY_OPTIONS_LOOKUP_ISSUER);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;JWT payload:&lt;/p&gt;
&lt;figure class=&#34;highlight json&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;sub&amp;quot;&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;mihael@rpgnextgen.com&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;attr&#34;&gt;&amp;quot;iss&amp;quot;&lt;/span&gt; &lt;span class=&#34;punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;string&#34;&gt;&amp;quot;https://rpgnextgen.com&amp;quot;&lt;/span&gt;&lt;span class=&#34;punctuation&#34;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The above payload of a JWT token would match the first configuration because the issuer claim
matches the id (second parameter) on the &lt;code&gt;il_jwt_addVerifyOptions&lt;/code&gt; call.&lt;/p&gt;
&lt;h2 id=&#34;Wrap-Up&#34;&gt;&lt;a href=&#34;#Wrap-Up&#34; class=&#34;headerlink&#34; title=&#34;Wrap Up&#34;&gt;&lt;/a&gt;Wrap Up&lt;/h2&gt;&lt;p&gt;Besides some changes in the length of some template variables the JWT plugin has gotten some 
major update. Thanks for the contributions.&lt;/p&gt;
&lt;p&gt;Happy web serving!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2024/01/21/2024-01-21-ileastic-load-balancing/</guid>
            <title>Tour of Champions - Load Balancing</title>
            <link>http://example.com/2024/01/21/2024-01-21-ileastic-load-balancing/</link>
            <category>RPG</category>
            <category>IBMi</category>
            <pubDate>Sun, 21 Jan 2024 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;From time to time you may enouter the situation where you have a very well written web service 
which works great for 95% of time but you have some spikes where there is more traffic on those 
services as usual and much more than one instance can handle.&lt;/p&gt;
&lt;p&gt;One approach is to look for some ways to optimize things so it could also handle those spikes.
If an optimization results in being able to handle the traffic spikes it would be the ideal 
situation as this would keep things simple and “simple” is something I really like ;-).&lt;/p&gt;
&lt;p&gt;But sometimes there is not much we can do about the performance of a single web service 
instance any more. But if one instance cannot handle things then maybe some more instances
can.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Welcome to the land of load balancing!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;Load-Balancing&#34;&gt;&lt;a href=&#34;#Load-Balancing&#34; class=&#34;headerlink&#34; title=&#34;Load Balancing&#34;&gt;&lt;/a&gt;Load Balancing&lt;/h2&gt;&lt;p&gt;Here some definition for load balancing from Wikipedia (yeah … shame on me for using Wikipedia 
as a reference ;-) ):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In computing, load balancing refers to the process of distributing a set of tasks over a set of 
resources (computing units), with the aim of making their overall processing more efficient.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So we have a set of tasks (multiple HTTP requests) which we would like to distribute over a set 
of resources (multiple instances (jobs) of a web service).&lt;/p&gt;
&lt;p&gt;Starting the web service program in multiple jobs (on a different port for each job) is really no
problem on our beloved IBM i. There are so many ways to skin this cat like &lt;code&gt;SBMJOB&lt;/code&gt; or using 
autostart jobs in a subsystem or … . Choose the variant which best fits your environment.&lt;/p&gt;
&lt;p&gt;So now we have the set of resources set up and we can easily generate our set of tasks (HTTP 
requests) with tools like &lt;a href=&#34;https://k6.io/&#34;&gt;k6&lt;/a&gt; or &lt;a href=&#34;https://gatling.io/&#34;&gt;Gatling&lt;/a&gt;. The missing piece 
of the puzzle is the middleware for the load balancing itself.&lt;/p&gt;
&lt;p&gt;But you don’t have to look any further. One possible solution is sitting right in front of your nose.
It is included in every IBM i installation and is installed on millions of servers.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Apache HTTP Server!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;Apache-HTTP-Server&#34;&gt;&lt;a href=&#34;#Apache-HTTP-Server&#34; class=&#34;headerlink&#34; title=&#34;Apache HTTP Server&#34;&gt;&lt;/a&gt;Apache HTTP Server&lt;/h2&gt;&lt;p&gt;The Apache HTTP server project support its features by including and enabling modules. All modules 
of the Apache HTTP server has been compiled as ILE modules and some of them are bound to the service 
program &lt;code&gt;QHTTPSVR/QZSRCORE&lt;/code&gt;. At least this service program contains the HTTP server modules which 
are needed for setting up load balancing for the HTTP server.&lt;/p&gt;
&lt;p&gt;The necessary modules are &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mod_proxy&lt;/li&gt;
&lt;li&gt;mod_proxy_http&lt;/li&gt;
&lt;li&gt;mod_proxy_balancer&lt;/li&gt;
&lt;li&gt;load balancer scheduler algorithm module like mod_lbmethod_byrequests&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The modules will be loaded with the &lt;code&gt;LoadModule&lt;/code&gt; directive. The syntax is very simple:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;LoadModule &amp;lt;module identifier&amp;gt; &amp;lt;file&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The module identifier of the module can be found in the Apache HTTP documentation for 
&lt;a href=&#34;https://httpd.apache.org/docs/2.4/mod/&#34;&gt;modules&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Most tutorials and documentation for the Apache HTTP server is for the Linux operating system. The
corresponding object of a service program in linux is a shared object (suffix &lt;code&gt;.so&lt;/code&gt;). So if you
see a linux tutorial with this line for loading a module&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;LoadModule proxy_module modules/mod_proxy.so&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;it corresponds to this line on IBM i:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;LoadModule proxy_module /QSYS.LIB/QHTTPSVR.LIB/QZSRCORE.SRVPGM&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;So loading the modules necessary for load balancing looks like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;LoadModule proxy_module /QSYS.LIB/QHTTPSVR.LIB/QZSRCORE.SRVPGM&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;LoadModule proxy_http_module /QSYS.LIB/QHTTPSVR.LIB/QZSRCORE.SRVPGM&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;LoadModule proxy_balancer_module /QSYS.LIB/QHTTPSVR.LIB/QZSRCORE.SRVPGM&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;LoadModule lbmethod_byrequests_module /QSYS.LIB/QHTTPSVR.LIB/QZSRCORE.SRVPGM&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The rest of the configuration is compatible with the standard documentation and may look like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ProxyRequests Off&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;Proxy \*&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Order deny,allow&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Deny from all&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/Proxy&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;Proxy balancer://clusterIleastic&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    BalancerMember http://devil01:47001&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    BalancerMember http://devil01:47002&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ProxySet lbmethod=byrequests&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/Proxy&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;Location /balancer-manager&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    SetHandler balancer-manager&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/Location&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ProxyPass /balancer-manager !&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ProxyPass / balancer://clusterIleastic/&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Note: I am no expert of Apache HTTP server. This is a simple configuration for my private adventures
      and should definitely be reviewed before using it anywhere.&lt;/p&gt;
&lt;h2 id=&#34;ILEastic-Web-Services&#34;&gt;&lt;a href=&#34;#ILEastic-Web-Services&#34; class=&#34;headerlink&#34; title=&#34;ILEastic Web Services&#34;&gt;&lt;/a&gt;ILEastic Web Services&lt;/h2&gt;&lt;p&gt;As we can see “Load Balancing” is not something specific about ILEastic. It is rather the opposite:
it has nothing directly to do with ILEastic. But ILEastic web services can be “load balanced” in a 
very easy way because an ILEastic web service brings everything to the table it needs … even its 
own web server. So all you really need to do is keep the port number configurable and you are ready 
to go.&lt;/p&gt;
&lt;p&gt;Starting a new instance of an ILEastic web service is a simple &lt;code&gt;SBMJOB&lt;/code&gt; and stopping an instance
is done with &lt;code&gt;ENDJOB&lt;/code&gt;. No magic there.&lt;/p&gt;
&lt;p&gt;It really can be that simple!&lt;/p&gt;
&lt;p&gt;Happy load balancing!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2024/01/08/2024-01-08%20i%20means%20integration%20-%20part%206/</guid>
            <title>i means integration - Receiving data with Helidon</title>
            <link>http://example.com/2024/01/08/2024-01-08%20i%20means%20integration%20-%20part%206/</link>
            <category>Java</category>
            <category>IBMi</category>
            <pubDate>Mon, 08 Jan 2024 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;When the IBM i community talks about programming languages on the IBM i it is mostly about RPG and 
CL. Sometimes C also trickles into the conversion but mostly that’s about it. Most people would not 
give Java a second thought on the IBM i even though many IBM products are based on Java and are 
running very well on the i.&lt;/p&gt;
&lt;p&gt;Java frameworks and libraries are very mature. And with organizations like Eclipse and Apache you get
really high quality software.&lt;/p&gt;
&lt;p&gt;What may hinder the usage of Java on IBM i is the release cycle of the Java VM and runtime from IBM.
IBM releases “only” Long Term Support (LTS) releases of Java for IBM i. We had Java 11 on IBM i and 
it took a long time till IBM released Java 17. This really hinders adoption as the major players are 
dashing forward with ever shorter release cycles and more current versions of dependencies in their 
requirements.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://helidon.io/&#34;&gt;Helidon&lt;/a&gt; microservice framework is a good match when it comes to releases 
and requirements as you can use Helidon version 2 which is supported by Oracle and run it with Java 
11 on older IBM i versions. But IBM has released Java 17 for IBM i some time ago so that Helidon 
version 3 can now also run on IBM i which comes with some improvements concerning messaging. With 
Helidon 3 Oracle jumped from MicroProfile Messaging 1.0 to &lt;a href=&#34;https://download.eclipse.org/microprofile/microprofile-reactive-messaging-3.0/microprofile-reactive-messaging-spec-3.0.html&#34;&gt;MicroProfile Messaging 3.0&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And MicroProfile Messaging is the key component of this post as it makes it very easy to receive
data from message queuing systems like &lt;a href=&#34;https://activemq.apache.org/components/classic/&#34;&gt;ActiveMQ (Classic)&lt;/a&gt;
or &lt;a href=&#34;https://activemq.apache.org/components/artemis/&#34;&gt;Artemis&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Could this have been possible earlier with Java on IBM i?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yes, definitely! But with the advent of microservices and the corresponding specs in Java
implementations and projects in general it is now much easier to do it.&lt;/p&gt;
&lt;p&gt;For the examples in this post I will use the Helidon framework version 3.&lt;/p&gt;
&lt;h2 id=&#34;Receiving-Data&#34;&gt;&lt;a href=&#34;#Receiving-Data&#34; class=&#34;headerlink&#34; title=&#34;Receiving Data&#34;&gt;&lt;/a&gt;Receiving Data&lt;/h2&gt;&lt;p&gt;Originally the RPG program had sent the data as one big blob of bytes. And for as long as we stay
in our RPG bubble this would have worked. Would it also work for Java? Yes, but it would take some
extra steps. With the JTOpen library we could define a record format (see RFML, Record class, 
RecordFormat class, RecordFormatDocument class) and feed the data to that Record or 
RecordFormatDocument instance. Then we could extract the single pieces of data we need from that 
instance.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Does not sound very straight forward, doesn’t it?!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But as we also have the choice to send the data in JSON format we can simplify things a lot on the
Java side because then the framework(s) is doing all the heavy lifting for us. For getting the data
for an auction we can just code the following.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@Inject&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ObjectMapper mapper;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@Incoming(&amp;quot;auction-events&amp;quot;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@Acknowledgment(Acknowledgment.Strategy.MANUAL)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; CompletionStage&amp;lt;Void&amp;gt; &lt;span class=&#34;title function_&#34;&gt;receiveMessage&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;(JmsMessage&amp;lt;String&amp;gt; message)&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;type&#34;&gt;AuctionEvent&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;event&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; mapper.readValue(message.getPayload(), AuctionEvent.class);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        handleEvent(event);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; message.ack();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125; &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (Exception e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        logger.log(Level.SEVERE, &lt;span class=&#34;string&#34;&gt;&amp;quot;Error on processing auction event&amp;quot;&lt;/span&gt;, e);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; message.nack(e);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;That is pretty straight forward :).&lt;/p&gt;
&lt;p&gt;What makes the code also very short is the fact that we can get an Auction object with a one-liner
by using the Jackson library to deserialize the JSON data. Very nice library!&lt;/p&gt;
&lt;p&gt;Note: I decided to manually acknowledge the messages as this allows us to “not acknowledge” a 
message which then stays in the queue and won’t be lost. This is not the ideal solution as now
the message will block any further processing. You need to find a strategy which best fits your 
use case.&lt;/p&gt;
&lt;h3 id=&#34;Artemis&#34;&gt;&lt;a href=&#34;#Artemis&#34; class=&#34;headerlink&#34; title=&#34;Artemis&#34;&gt;&lt;/a&gt;Artemis&lt;/h3&gt;&lt;p&gt;The above solution for receiving data would be ideal. But as we all know we don’t always have the
ideal situation in the real world. Mostly it looks a little bit more complicated and this is also
the case here.&lt;/p&gt;
&lt;p&gt;If we send the data with the STOMP client for ILE it will always send a &lt;code&gt;Content-Length&lt;/code&gt; header.
Artemis will always interpret the message as a &lt;code&gt;ByteMessage&lt;/code&gt; when using JMS because of this header,
see &lt;a href=&#34;https://activemq.apache.org/components/artemis/documentation/latest/stomp.html#sending-and-consuming-stomp-message-from-jms-or-core-api&#34;&gt;Artemis - STOMP and JMS interoperability&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But with the MicroProfile Spec we are super flexible when it comes to how to receive message
from a queue.&lt;/p&gt;
&lt;p&gt;To circumvent our problem we can inspect what type of message we got and then act accordingly.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@Inject&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; ObjectMapper mapper;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@Incoming(&amp;quot;auction-events&amp;quot;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@Acknowledgment(Acknowledgment.Strategy.MANUAL)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; CompletionStage&amp;lt;Void&amp;gt; &lt;span class=&#34;title function_&#34;&gt;receiveMessage&lt;/span&gt;&lt;span class=&#34;params&#34;&gt;(JmsMessage&amp;lt;?&amp;gt; message)&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;type&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;messageText&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;null&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;try&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// content-length header available -&amp;gt; ByteMessage else TextMessage&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (message &lt;span class=&#34;keyword&#34;&gt;instanceof&lt;/span&gt; JmsTextMessage) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            messageText = ((JmsTextMessage) message).getPayload();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125; &lt;span class=&#34;keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (message &lt;span class=&#34;keyword&#34;&gt;instanceof&lt;/span&gt; JmsBytesMessage) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            messageText = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;String&lt;/span&gt;((&lt;span class=&#34;type&#34;&gt;byte&lt;/span&gt;[]) message.getPayload(), StandardCharsets.UTF_8);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125; &lt;span class=&#34;keyword&#34;&gt;else&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;RuntimeException&lt;/span&gt;(&lt;span class=&#34;string&#34;&gt;&amp;quot;Unknown JMS message type &amp;quot;&lt;/span&gt; + message.getClass().getName());&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;type&#34;&gt;AuctionEvent&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;event&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; mapper.readValue(messageText, AuctionEvent.class);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        handleEvent(event);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; message.ack();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125; &lt;span class=&#34;keyword&#34;&gt;catch&lt;/span&gt; (Exception e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        logger.log(Level.SEVERE, &lt;span class=&#34;string&#34;&gt;&amp;quot;Error on processing auction event&amp;quot;&lt;/span&gt;, e);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; message.nack(e);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;In our example we use the well established library &lt;code&gt;Jackson&lt;/code&gt; to convert the data from JSON to an
auction object.&lt;/p&gt;
&lt;h2 id=&#34;Logging&#34;&gt;&lt;a href=&#34;#Logging&#34; class=&#34;headerlink&#34; title=&#34;Logging&#34;&gt;&lt;/a&gt;Logging&lt;/h2&gt;&lt;p&gt;If you want to integrate the logs of your Java application into any central logging server then 
one way to achieve this is to send you logs directly to the logging server via a socket connection.
The Java Util Logging framework supports this by using the &lt;code&gt;SocketHandler&lt;/code&gt; class. &lt;/p&gt;
&lt;p&gt;Note: You may want to reimplement the &lt;code&gt;SocketHandler&lt;/code&gt; so that it reconnects in case it lost the
connection to the logging server.&lt;/p&gt;
&lt;h3 id=&#34;Formatter&#34;&gt;&lt;a href=&#34;#Formatter&#34; class=&#34;headerlink&#34; title=&#34;Formatter&#34;&gt;&lt;/a&gt;Formatter&lt;/h3&gt;&lt;p&gt;The &lt;code&gt;SocketHandler&lt;/code&gt; class will by default send every log entry to the log server in XML format. If 
you need to transfer the log entries in JSON this can be done very easily by implementing the 
&lt;code&gt;java.util.logging.Formatter&lt;/code&gt; interface.&lt;/p&gt;
&lt;p&gt;To create the JSON string you can use the &lt;code&gt;ObjectMapper&lt;/code&gt; class from the Jackson library. To get
a root node you can call &lt;code&gt;ObjectMapper::createObjectNode&lt;/code&gt;. For adding values to the root node you
can call the corresponding &lt;code&gt;put&lt;/code&gt; method from the &lt;code&gt;ObjectMapper&lt;/code&gt; class.&lt;/p&gt;
&lt;h3 id=&#34;Custom-Data&#34;&gt;&lt;a href=&#34;#Custom-Data&#34; class=&#34;headerlink&#34; title=&#34;Custom Data&#34;&gt;&lt;/a&gt;Custom Data&lt;/h3&gt;&lt;p&gt;By default only the data from the log entry is sent to the log server. You may want to transfer 
additional data to the log server, f. e. the application name so that you know to which application
the log entries belong to.&lt;/p&gt;
&lt;p&gt;For static data this can be added to the configuration of the formatter.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;rpgnextgen.log.JsonFormatter.application = rpgnextgen.helidon.mq&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The formatter can query the &lt;code&gt;LogManager&lt;/code&gt; for any logging configuration data.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;type&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;application&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; logManager.getProperty(&lt;span class=&#34;string&#34;&gt;&amp;quot;rpgnextgen.log.JsonFormatter.application&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;And later add it to the log entry.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;type&#34;&gt;ObjectNode&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;root&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; mapper.createObjectNode();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;root.put(&lt;span class=&#34;string&#34;&gt;&amp;quot;app&amp;quot;&lt;/span&gt;, application);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;h2 id=&#34;Dependencies&#34;&gt;&lt;a href=&#34;#Dependencies&#34; class=&#34;headerlink&#34; title=&#34;Dependencies&#34;&gt;&lt;/a&gt;Dependencies&lt;/h2&gt;&lt;p&gt;With most other programming languages it takes a lot of effort to choose the libraries you want 
to use. Best negative example is the world of JavaScript&amp;#x2F;TypeScript.&lt;/p&gt;
&lt;p&gt;In our Java project using Helidon we have almost solely dependencies from &lt;a href=&#34;https://apache.org/&#34;&gt;Apache&lt;/a&gt;,
&lt;a href=&#34;https://eclipse.org/&#34;&gt;Eclipse&lt;/a&gt;, &lt;a href=&#34;https://jboss.org/&#34;&gt;JBoss&lt;/a&gt; and &lt;a href=&#34;https://oracle.com/&#34;&gt;Oracle&lt;/a&gt;. Every 
one of them is offering high quality software packages. The remaining dependencies can be easily 
looked up (like the &lt;a href=&#34;https://netty.io/&#34;&gt;Netty&lt;/a&gt; project or &lt;a href=&#34;https://fasterxml.com/&#34;&gt;FasterXML&lt;/a&gt;).
This makes it much easier to check dependencies and maintain the application.&lt;/p&gt;
&lt;p&gt;Happy integrating!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2023/12/10/2023-12-10%20i%20means%20integration%20-%20part%205/</guid>
            <title>i means integration - Receiving data with STOMP client</title>
            <link>http://example.com/2023/12/10/2023-12-10%20i%20means%20integration%20-%20part%205/</link>
            <category>RPG</category>
            <category>IBMi</category>
            <pubDate>Sun, 10 Dec 2023 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;In the last post we have sent a message to a message queue on the Artemis server. We did this
by utilizing the STOMP client service program available at &lt;a href=&#34;https://repo.rpgnextgen.com/&#34;&gt;repo.rpgnextgen.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this part of the blog series we want to receive the message we sent in our &lt;a href=&#34;https://blog.rpgnextgen.com/2023/11/12/2023-11-12%20i%20means%20integration%20-%20part%204/&#34;&gt;last episode&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The message was sent with the content type &lt;code&gt;application/octet-stream&lt;/code&gt; (constant &lt;code&gt;STOMP_MIME_BINARY&lt;/code&gt;) 
which describes that we sent a bunch of binary data. The data is encoded with the CCSID of the job.&lt;/p&gt;
&lt;p&gt;This works fine as long as we are staying on the same server and&amp;#x2F;or have full control over our 
environment. But it may not work so good if different parties need to communicate with each
other.&lt;/p&gt;
&lt;p&gt;So how does the code look like for receiving data?&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1   **FREE&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3   ctl-opt main(main);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5   /include &amp;#x27;stomp.rpginc&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7  dcl-proc main;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8      dcl-s client pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9      dcl-s frame pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10     dcl-ds frameBuffer likeds(stomp_util_buffer_t);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12     client = stomp_create(&amp;#x27;127.0.0.1&amp;#x27; : 40002);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13     stomp_setClientId(client : &amp;#x27;mihael&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15     stomp_open(client);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16     stomp_command_connect(client : &amp;#x27;admin&amp;#x27; : &amp;#x27;udontknow&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17     stomp_command_subscribe(client : &amp;#x27;auction-events&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19     // start receiving data&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20     frame = stomp_receiveFrame(client);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21     dow (frame &amp;lt;&amp;gt; *null);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22         if (stomp_frame_getCommand(frame) = &amp;#x27;ERROR&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23             dsply &amp;#x27;An error occured&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24             leave;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25         endif;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27         // display message content&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28         frameBuffer = stomp_frame_getBody(frame);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29         message_info(%str(frameBuffer.data : frameBuffer.length));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30         stomp_frame_finalize(frame);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32         // receive next frame&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33         frame = stomp_receiveFrame(client);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34     enddo;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36     stomp_command_disconnect(client);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37     stomp_close(client);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39     on-exit;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40         stomp_finalize(client);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41 end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Lets have a look at the code line by line. We start exactly the same as the sending programing.&lt;/p&gt;
&lt;p&gt;Line 1  : We are writing fully free RPG code. Not half-assing this already from the start ;-)&lt;br&gt;Line 3  : We will compile this program in two steps. Thus we only declare the main procedure in the control options.&lt;br&gt;Line 5  : The STOMP copybook needs to be included so that we can use the constants and prototypes from the STOMP project. Everything else will be added to the compile commands.&lt;br&gt;Line 8  : For the client a pointer variable is declared. We are not interested what lies behind that pointer. We just pass it to the STOMP procedures.&lt;br&gt;Line 9  : A variable for the received frames is declared.&lt;br&gt;Line 10 : The frame body is accessed via a pointer. The &lt;code&gt;frameBuffer&lt;/code&gt; data structure contains this pointer (data) and the length of the data which lies behind the data pointer.&lt;br&gt;Line 12 : An instance of the STOMP client is created. The client is accessed via the pointer. The internal data of the client doesn’t matter to us. We are only using the procedures from the STOMP project anyway. The host and port we want to connect to are passed to the create procedure. The port should be the same we used in the Artemis configuration for our STOMP acceptor.&lt;br&gt;Line 13 : A client id is set on the client. This id can be used to identify this specific client connection to the server in the Artemis web console. The id is just a string and can be set to any value.&lt;br&gt;Line 15 : A socket connection to the Artemis server instance is created.&lt;br&gt;Line 16 : To connect on a (STOMP) protocol level we need to send a STOMP CONNECT frame&amp;#x2F;command to the server. This also includes the credentials used to login to the message broker. For an anonymous connect we can just pass the client pointer.&lt;br&gt;Line 17 : We need to tell the Artemis instance from which queue we want to receive messages. This is done by subscribing to a queue or topic (or in Artemis lingo: address).&lt;br&gt;Line 20 - 21 : Here we are starting the loop for receiving messages (STOMP lingo: frames) from the Artemis instance. The program will wait here till the next message has been received. &lt;code&gt;stomp_receiveFrame&lt;/code&gt; will return &lt;code&gt;*NULL&lt;/code&gt; if the timeout has been reached before any frame has been received. We can change the timeout on the client by calling &lt;code&gt;stomp_setTimeout&lt;/code&gt;.&lt;br&gt;Line 22 : The server may also send an &lt;a href=&#34;https://stomp.github.io/stomp-specification-1.2.html#ERROR&#34;&gt;ERROR&lt;/a&gt; frame to indicate that something went wrong. We need to be ready for that and react accordingly.&lt;br&gt;Line 28 : We are mostly interested in the body of the message. We get that with &lt;code&gt;stomp_frame_getBody&lt;/code&gt;. The returned data structure contains a pointer to the message body and a subfield for the length. We could also get the headers of the frame with &lt;code&gt;stomp_frame_listHeaders&lt;/code&gt; and &lt;code&gt;stomp_frame_getHeader&lt;/code&gt; if we needed to.&lt;br&gt;Line 29 : We are expecting some string data in the same CCSID as our job. Because of that we can use the &lt;code&gt;%str&lt;/code&gt; built-in function which returns a string based on the passed pointer.&lt;br&gt;Line 30 : Evey stomp frame we receive has its memory dynamically allocated. We need to free this memory by calling &lt;code&gt;stomp_frame_finalize&lt;/code&gt;.&lt;br&gt;Line 36 : To properly disconnect from the Artemis server instance after we have finished the loop we need to send a &lt;code&gt;DISCONNECT&lt;/code&gt; frame to the server.&lt;br&gt;Line 37 : After disconnecting we can close the socket connection.&lt;br&gt;Line 40 : At the end we also need to free the memory allocated by the client.  &lt;/p&gt;
&lt;p&gt;We will see the content of the message in the job log (output by the &lt;code&gt;message_info&lt;/code&gt; procedure 
from the &lt;a href=&#34;https://bitbucket.org/m1hael/message&#34;&gt;MESSAGE&lt;/a&gt; service program).&lt;/p&gt;
&lt;h2 id=&#34;Durable-Subscriber&#34;&gt;&lt;a href=&#34;#Durable-Subscriber&#34; class=&#34;headerlink&#34; title=&#34;Durable Subscriber&#34;&gt;&lt;/a&gt;Durable Subscriber&lt;/h2&gt;&lt;p&gt;On subscribing to a message queue Artemis will create a queue for our client which receives all
the messages for which we have subscribed. For as long as our program runs we will receive those 
messages. But Artemis will delete this queue (not the address) when we disconnect. This means that 
we might miss some messages. STOMP has no concept of a persistent subscriptions. But Artemis supports 
&lt;a href=&#34;https://activemq.apache.org/components/artemis/documentation/latest/stomp.html#durable-subscriptions&#34;&gt;Durable Subscriptions&lt;/a&gt;
when using the STOMP protocol. From the Artemis docs:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;To create a durable subscription the client-id header must be set on the CONNECT frame and 
the durable-subscription-name must be set on the SUBSCRIBE frame. The combination of these 
two headers will form the identity of the durable subscription.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The STOMP service program has dedicated procedure for setting those values.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-s client pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;client = stomp_create(&amp;#x27;127.0.0.1&amp;#x27; : 40002);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;stomp_setClientId(client : &amp;#x27;mihael&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;stomp_setDurableSubscriberName(client : &amp;#x27;auctions&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now we won’t miss any messages.&lt;/p&gt;
&lt;h2 id=&#34;Content-Type&#34;&gt;&lt;a href=&#34;#Content-Type&#34; class=&#34;headerlink&#34; title=&#34;Content Type&#34;&gt;&lt;/a&gt;Content Type&lt;/h2&gt;&lt;p&gt;Currently the message itself doesn’t convey anything about the message itself. The content type is
just &lt;code&gt;application/octet-stream&lt;/code&gt; which means that it can be anything. It is just a bunch of bytes.
The receiving program needs to know a lot about the sending program and thus has a tight coupling
to it. And as long as we just want to decouple parts of the execution of process and have full
control over the environment this might by ok.&lt;/p&gt;
&lt;p&gt;But when other players enter the game this situation might not be so convenient. The data format
is just a data structure (which doesn’t have any meaning anywhere other than RPG where we could
share the data structure via a copy book). The encoding (EBCDIC) is also not very mainstream and 
thus not necessarily generally available on other platforms &amp;#x2F; programming languages.&lt;/p&gt;
&lt;p&gt;If a Java program needs to receive an auction message it could use the JTOpen project for getting
things together (data format and encoding) but that would be rather tedious.&lt;/p&gt;
&lt;p&gt;If a Node.js program needs to receive this message you would be rather hard pressed to get things
to work.&lt;/p&gt;
&lt;h3 id=&#34;Solution&#34;&gt;&lt;a href=&#34;#Solution&#34; class=&#34;headerlink&#34; title=&#34;Solution&#34;&gt;&lt;/a&gt;Solution&lt;/h3&gt;&lt;p&gt;One possible solution would be to send the data as a JSON string. JSON is a very universal format
and has the default encoding of UTF-8 which is also widely available. Thus you are not limited to 
your own platform but are also able to invite other players into the game without having to
change anything on your side.&lt;/p&gt;
&lt;h2 id=&#34;Multiple-Formats&#34;&gt;&lt;a href=&#34;#Multiple-Formats&#34; class=&#34;headerlink&#34; title=&#34;Multiple Formats&#34;&gt;&lt;/a&gt;Multiple Formats&lt;/h2&gt;&lt;p&gt;On the receiving side we might want to support different formats or versions of the data. One 
way to implement this would be to publish the format on you intranet &amp;#x2F; extranet in some way
and use a custom content type. Content types are not limit by the strings we see so often on 
the internet like &lt;code&gt;application/json&lt;/code&gt; or &lt;code&gt;text/html&lt;/code&gt;. You can use whatever you want though there
are some best practices, see &lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc6838#section-3.4&#34;&gt;Media Type Specifications&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If we want to convey that this is an auction we could use the content type &lt;code&gt;application/x.rpgnextgen.auction&lt;/code&gt;.
There is an established way of adding additional information like format and version to the
content type by appending the info with an add sign.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;application/x.rpgnextgen.auction+json+v1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now the receiver knows pretty well from the message alone what he can expect of the message body.&lt;/p&gt;
&lt;p&gt;We can query the frame header by using &lt;code&gt;stomp_frame_getHeader&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;stomp_frame_getHeader(frame : &amp;#x27;content-type&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;h2 id=&#34;JSON&#34;&gt;&lt;a href=&#34;#JSON&#34; class=&#34;headerlink&#34; title=&#34;JSON&#34;&gt;&lt;/a&gt;JSON&lt;/h2&gt;&lt;p&gt;The &lt;a href=&#34;https://github.com/sitemule/noxDB&#34;&gt;noxDB&lt;/a&gt; project in its master branch supports parsing a
JSON string from a pointer variable out-of-the-box. We can just pass the pointer from the frame 
buffer to noxDB.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-ds frameBuffer likeds(stomp_util_buffer_t);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-s json pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;frame = stomp_receiveFrame(client);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;json = jx_parseString(frameBuffer.data);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;jx_close(json);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;In the utf8 branch which is packaged for use with &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg&#34;&gt;iPKG&lt;/a&gt;
we need to take a little extra effort into account as we cannot directly pass the pointer.
We need to pass the data into a string variable and pass that extra variable to the noxDB
procedure.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-ds frameBuffer likeds(stomp_util_buffer_t);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-s json pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-s string like(UTF8);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;frame = stomp_receiveFrame(client);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;frameBuffer = stomp_frame_getBody(frame);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;string = %str(frameBuffer.data : frameBuffer.length);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;json = nox_parseString(string);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;jx_close(json);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;h2 id=&#34;API-Documentation&#34;&gt;&lt;a href=&#34;#API-Documentation&#34; class=&#34;headerlink&#34; title=&#34;API Documentation&#34;&gt;&lt;/a&gt;API Documentation&lt;/h2&gt;&lt;p&gt;You can find the API documentation of the STOMP for ILE project and noxDB project at 
&lt;a href=&#34;https://iledocs.rpgnextgen.com/&#34;&gt;iledocs.rpgnextgen.com&lt;/a&gt;. 
For those interested in generating API documentation for ILE programs and service programs:
The documentation at iledocs.rpgnextgen.com has been generated with the project 
&lt;a href=&#34;https://bitbucket.org/m1hael/iledocs-page-builder&#34;&gt;iledocs-page-builder&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy integrating!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2023/11/12/2023-11-12%20i%20means%20integration%20-%20part%204/</guid>
            <title>i means integration - Sending data with STOMP client</title>
            <link>http://example.com/2023/11/12/2023-11-12%20i%20means%20integration%20-%20part%204/</link>
            <category>RPG</category>
            <category>IBMi</category>
            <pubDate>Sun, 12 Nov 2023 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;In the last posts we have set up all necessary building blocks for sending data to a message queue
on the Apache Artemis instance on IBM i. Now we are putting all those pieces together and write
an ILE RPG progam which sends data to a message queue with the STOMP service program.&lt;/p&gt;
&lt;p&gt;We are picking up the case from the &lt;a href=&#34;https://blog.rpgnextgen.com/2023/10/11/2023-10-11%20i%20means%20integration%20-%20part%201/&#34;&gt;first part&lt;/a&gt; 
of this blog series. We want to inform that a new auction has started including the items that 
will be part of the auction.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1  **FREE&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3  ctl-opt main(main);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5  /include &amp;#x27;stomp.rpginc&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7  dcl-ds auction_t qualified template;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8     id uns(20);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9     title varchar(100);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10     description varchar(1000);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11     items likeds(auction_item_t) dim(10);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12     auctionStart timestamp;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13     auctionEnd timestamp;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14 end-ds;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16 dcl-ds auction_item_t qualified template;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17     id uns(10);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18     name varchar(100);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19     quantity uns(3);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20 end-ds;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22 dcl-proc main;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23     dcl-s client pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24     dcl-ds auction likeds(auction_t);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26     auction = fillAuctionData();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28     client = stomp_create(&amp;#x27;localhost&amp;#x27; : 40002);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29     stomp_open(client);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30     stomp_command_connect(client : &amp;#x27;auction_user&amp;#x27; : &amp;#x27;uwillneverknow&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31     stomp_command_send(client : &amp;#x27;auction-events&amp;#x27; : %addr(auction) : %size(auction) : STOMP_MIME_BINARY);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32     stomp_command_disconnect(client);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33     stomp_close(client);   &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34     &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35     on-exit;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36         stomp_finalize(client);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37 end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;This looks like a lot of code but the &lt;code&gt;main&lt;/code&gt; procedure is just 16 lines. So it is not much at all.
Let’s look at the code line by line.&lt;/p&gt;
&lt;p&gt;Line 1  : We are writing fully free RPG code. We are not half-assing this already from the start ;-)&lt;br&gt;Line 3  : We will compile this program in two steps. Thus we only declare the main procedure in the control options.&lt;br&gt;Line 5  : The STOMP copybook needs to be included so that we can use the constants and prototypes from the STOMP project. Everything else will be added to the compile commands.&lt;br&gt;Line 7 - 20 : The definition of the auction data which will be sent to the message queue.&lt;br&gt;Line 26 : The data is loaded here. Nothing fancy.&lt;br&gt;Line 28 : An instance of the STOMP client is created. The client is accessed via the pointer. The internal data of client doesn’t matter to us. We are only using the procedures from the STOMP project anyway. The host and port we want to connect to are passed to the create procedure. The port should be the same we used in the Artemis configuration for our STOMP acceptor.&lt;br&gt;Line 29 : A socket connection to the message broker is created.&lt;br&gt;Line 30 : To connect on a (STOMP) protocol level we need to send a STOMP frame&amp;#x2F;command to the server. This also includes the credentials used to login to the message broker. For an anonymous connect we can just pass the client.&lt;br&gt;Line 31 : Now here we are sending the data at last. It will be sent as a block of bytes. The MIME type also states that it is no text data but raw bytes which are the payload of the message.&lt;br&gt;Line 32 : If no more message are to be sent the connection can be closed (on a protocol level).&lt;br&gt;Line 33 : The socket connection needs to be closed.&lt;br&gt;Line 36 : Last but not least the memory allocated by the client needs to be freed.&lt;/p&gt;
&lt;p&gt;We can see the message in the &lt;code&gt;sink&lt;/code&gt; queue we configured in the Artemis server. But there are some 
suboptimal things we can improve.&lt;/p&gt;
&lt;h2 id=&#34;Character-Encoding&#34;&gt;&lt;a href=&#34;#Character-Encoding&#34; class=&#34;headerlink&#34; title=&#34;Character Encoding&#34;&gt;&lt;/a&gt;Character Encoding&lt;/h2&gt;&lt;p&gt;The data sent to the message queue is in the CCSID of the current job. By default this is stated
nowhere in the message. So the receiver of the message has no idea what the encoding of the message
content is. One way of dealing with this is adding the &lt;code&gt;charset&lt;/code&gt; parameter to the &lt;code&gt;Content-Type&lt;/code&gt; with
the correct charset for the current CCSID, like &lt;code&gt;application/octet-stream; charset=x-ebcdic-germany&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But EBCDIC is not a common character encoding and thus not a good encoding for data exchange as the
sender doesn’t know who will receive the message.&lt;/p&gt;
&lt;h2 id=&#34;Sending-the-message-in-the-JSON-format&#34;&gt;&lt;a href=&#34;#Sending-the-message-in-the-JSON-format&#34; class=&#34;headerlink&#34; title=&#34;Sending the message in the JSON format&#34;&gt;&lt;/a&gt;Sending the message in the JSON format&lt;/h2&gt;&lt;p&gt;The message we sent in the above code is hard to read as is because it was sent as a sequence of 
bytes. We used the data structure directly as the format of the data sent to the message queue so
every message receiver must also know the definition of the used data structure. As this is RPG 
specific it is not a good choice for data exchange. It would be better to use a data format which 
is not programming language specific … like &lt;a href=&#34;https://json.org/&#34;&gt;JSON&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Currently many services in the whole world exchange data in JSON format. We will also use this as
it is a very common and nice format which is also easily readably by humans.&lt;/p&gt;
&lt;p&gt;This also solves the “problem” with the character encoding as JSON is by default encoded in UTF-8.&lt;/p&gt;
&lt;h3 id=&#34;noxDB&#34;&gt;&lt;a href=&#34;#noxDB&#34; class=&#34;headerlink&#34; title=&#34;noxDB&#34;&gt;&lt;/a&gt;noxDB&lt;/h3&gt;&lt;p&gt;On IBM i there are several ways to generate JSON. Personally I like the &lt;a href=&#34;https://github.com/sitemule/noxDB&#34;&gt;noxDB&lt;/a&gt; 
project. It is a fast and flexible JSON software library available as a ILE service program. It 
supports the &lt;code&gt;data-gen&lt;/code&gt; opcode to easily generate JSON based on a data structure but also lets 
you create a fined tuned JSON document if you want to. So you have total control over the JSON 
generation. There are also some gems in the library like generating JSON based on a SQL result set.&lt;/p&gt;
&lt;p&gt;The project comes in two flavors: version 1 &amp;#x3D; EBCDIC based , version 2 &amp;#x3D; UTF-8 based&lt;/p&gt;
&lt;p&gt;The version 2 (UTF-8 based) is packaged as an RPM package and available in the 
&lt;a href=&#34;https://repo.rpgnextgen.com/&#34;&gt;RPM repository&lt;/a&gt; at &lt;a href=&#34;https://rpgnextgen.com/&#34;&gt;RPG Next Gen&lt;/a&gt;. 
It can be installed with the &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg&#34;&gt;iPKG&lt;/a&gt; client. The 
installation is as easy as always:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install noxdb2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ipkg install &amp;#x27;noxdb2-devel&amp;#x27; loc(&amp;#x27;/home/mihael/include&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;I won’t go into the details of how the noxDB service program can be used. There are dozens of
&lt;a href=&#34;https://github.com/sitemule/noxDB/tree/master/examples&#34;&gt;examples&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;stomp-command-send&#34;&gt;&lt;a href=&#34;#stomp-command-send&#34; class=&#34;headerlink&#34; title=&#34;stomp_command_send&#34;&gt;&lt;/a&gt;stomp_command_send&lt;/h3&gt;&lt;p&gt;We will rather take a look at the procedure that sends the data: &lt;code&gt;stomp_command_send&lt;/code&gt;&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-pr stomp_command_send extproc(*dclcase);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    client pointer const;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    destination varchar(100) const;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    messageData pointer const options(*string);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    messageLength uns(10) const options(*omit : *nopass);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    contentType varchar(100) const options(*omit : *nopass);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    headers pointer const options(*nopass);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-pr;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h4 id=&#34;Client&#34;&gt;&lt;a href=&#34;#Client&#34; class=&#34;headerlink&#34; title=&#34;Client&#34;&gt;&lt;/a&gt;Client&lt;/h4&gt;&lt;p&gt;As with most of the procedures we need to pass the client “object” (the pointer) to the procedure.&lt;/p&gt;
&lt;h4 id=&#34;Destination&#34;&gt;&lt;a href=&#34;#Destination&#34; class=&#34;headerlink&#34; title=&#34;Destination&#34;&gt;&lt;/a&gt;Destination&lt;/h4&gt;&lt;p&gt;Second we need to tell the procedure to which queue the message should be sent. This name maps
directly to a address name in Apache Artemis. In our case this is &lt;em&gt;auction-events&lt;/em&gt;.&lt;/p&gt;
&lt;h4 id=&#34;Message-Data&#34;&gt;&lt;a href=&#34;#Message-Data&#34; class=&#34;headerlink&#34; title=&#34;Message Data&#34;&gt;&lt;/a&gt;Message Data&lt;/h4&gt;&lt;p&gt;We notice that the parameter for the message data is a pointer with &lt;code&gt;options(*string)&lt;/code&gt;. The
procedure can work with a null-terminated string (meaning a string with the termination character
&lt;code&gt;x&amp;#39;00&amp;#39;&lt;/code&gt; at the end of it). It expects a null-terminated string if no message length is provided.&lt;/p&gt;
&lt;p&gt;In RPG this means that we can declare a variable &lt;code&gt;char&lt;/code&gt; or &lt;code&gt;varchar&lt;/code&gt; and end the data with &lt;code&gt;x&amp;#39;00&amp;#39;&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-s data varchar(100) ccsid(*utf8);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;data = &amp;#x27;&amp;#123; &amp;quot;id&amp;quot; : 358 &amp;#125;&amp;#x27; + x&amp;#x27;00&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;stomp_command_send(client : &amp;#x27;auction-events&amp;#x27; : %addr(data : *data));&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;But as &lt;code&gt;options(*string)&lt;/code&gt; has been declared on the paramter we can also just pass the variable.
No need to bother with any C-style string stuff.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-s data varchar(100) ccsid(*utf8);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;data = &amp;#x27;&amp;#123; &amp;quot;id&amp;quot; : 358 &amp;#125;&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;stomp_command_send(client : &amp;#x27;auction-events&amp;#x27; : data);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;This looks much better. :)&lt;/p&gt;
&lt;p&gt;It is also possible to pass a string literal.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;stomp_command_send(client : &amp;#x27;auction-events&amp;#x27; : &amp;#x27;&amp;#123; &amp;quot;id&amp;quot; : 358 &amp;#125;&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h4 id=&#34;Message-Length&#34;&gt;&lt;a href=&#34;#Message-Length&#34; class=&#34;headerlink&#34; title=&#34;Message Length&#34;&gt;&lt;/a&gt;Message Length&lt;/h4&gt;&lt;p&gt;Without passing the message length the procedure will try to determine the length by searching for
the first x’00’ value. This is not always very convenient. If you want to send binary data and the
data might have &lt;code&gt;x&amp;#39;00&amp;#39;&lt;/code&gt; as a value then you need to pass the length of the data as a parameter.&lt;/p&gt;
&lt;h4 id=&#34;Content-Type&#34;&gt;&lt;a href=&#34;#Content-Type&#34; class=&#34;headerlink&#34; title=&#34;Content Type&#34;&gt;&lt;/a&gt;Content Type&lt;/h4&gt;&lt;p&gt;Similar to HTTP message we also should specify the &lt;code&gt;Content-Type&lt;/code&gt; of the data we want to send so
that the receiver knows how to interpret the data.&lt;/p&gt;
&lt;p&gt;If we always want to the send the same type of data (meaning also the same &lt;code&gt;Content-Type&lt;/code&gt;) then
we can set the content type on the client and don’t have to pass it on every call.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;stomp_setContentType(client : &amp;#x27;application/prs.rpgnextgen.auction&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The content type passed to the send procedure will overwrite any global settings on the client.&lt;/p&gt;
&lt;h4 id=&#34;Headers&#34;&gt;&lt;a href=&#34;#Headers&#34; class=&#34;headerlink&#34; title=&#34;Headers&#34;&gt;&lt;/a&gt;Headers&lt;/h4&gt;&lt;p&gt;If we want some additional headers on the frame we send to the server we can pass them to the
send procedure.&lt;/p&gt;
&lt;p&gt;The headers parameter is declared as a pointer. This doesn’t tell us much. But it expects a list
created with the &lt;em&gt;Linked List&lt;/em&gt; service program. Each entry needs to be in the format of the data
structure &lt;code&gt;stomp_frame_header_t&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-s headers pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-ds header likeds(stomp_frame_header_t) inz;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;headers = list_create();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;header.key = &amp;#x27;server&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;header.value = &amp;#x27;ibmi.localhost&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;list_add(headers : %addr(header) : %size(header));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;stomp_command_send(client : &amp;#x27;auction-events&amp;#x27; : %addr(auction) : %size(auction) :&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    STOMP_MIME_BINARY : headers);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;on-exit;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    list_dispose(headers);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Don’t forget to free the allocated memory of the list by calling &lt;code&gt;list_dispose(headers)&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;Receipts&#34;&gt;&lt;a href=&#34;#Receipts&#34; class=&#34;headerlink&#34; title=&#34;Receipts&#34;&gt;&lt;/a&gt;Receipts&lt;/h2&gt;&lt;p&gt;To make sure that the server has successfully received the message sent to the server we can 
request to acknowledge the receiving of a message with a receipt. So for every message we send 
to the server the server sends back a RECEIPT frame to tell the client that the message has been
successfully received.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;stomp_useReceipts(client : *on);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Note: Receipts only make sure that the server successfully received the message. It does not mean
      that the message has been successfully distributed to any other client.&lt;/p&gt;
&lt;h2 id=&#34;Mark-message-as-persistent&#34;&gt;&lt;a href=&#34;#Mark-message-as-persistent&#34; class=&#34;headerlink&#34; title=&#34;Mark message as persistent&#34;&gt;&lt;/a&gt;Mark message as persistent&lt;/h2&gt;&lt;p&gt;By default messages sent via the STOMP protocol are not persistent, meaning they will vanish if 
the server is restarted. STOMP also supports persistent &amp;#x2F; durable messages. This can be achieved
by adding the header &lt;code&gt;persistent&lt;/code&gt; to a message, see &lt;code&gt;STOMP_OPTION_PERSISTENT&lt;/code&gt; which can be set
on the STOMP client instance.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;stomp_setPersistMessages(client : *on);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;API-Documentation&#34;&gt;&lt;a href=&#34;#API-Documentation&#34; class=&#34;headerlink&#34; title=&#34;API Documentation&#34;&gt;&lt;/a&gt;API Documentation&lt;/h2&gt;&lt;p&gt;You can find the API documentation of the STOMP for ILE project at &lt;a href=&#34;https://iledocs.rpgnextgen.com/&#34;&gt;iledocs.rpgnextgen.com&lt;/a&gt;. 
For those interested in generating API documentation for ILE programs and service programs:
The documentation at iledocs.rpgnextgen.com has been generated with the project 
&lt;a href=&#34;https://bitbucket.org/m1hael/iledocs-page-builder&#34;&gt;iledocs-page-builder&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy integrating!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2023/10/23/2023-10-23%20i%20means%20integration%20-%20part%203/</guid>
            <title>i means integration - STOMP</title>
            <link>http://example.com/2023/10/23/2023-10-23%20i%20means%20integration%20-%20part%203/</link>
            <category>RPG</category>
            <category>IBMi</category>
            <pubDate>Mon, 23 Oct 2023 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;I covered how to install &lt;a href=&#34;https://activemq.apache.org/components/artemis/&#34;&gt;Apache Artemis&lt;/a&gt; on 
IBM i in the last part of this series. Now we need a way to connect to Artemis from our ILE 
environment as this is the main environment in classic SMB IBM i shops.&lt;/p&gt;
&lt;p&gt;Lets take one step back and have a look at the internet where many different platforms and 
programming languages work together. HTTP is one of the main pillars of the world wide
web. It is a rather simple protocol (at least version 1.0 and 1.1) which can be implemented 
in probably any programming language.&lt;/p&gt;
&lt;p&gt;There is something similar in the messaging world: &lt;a href=&#34;https://stomp.github.io/&#34;&gt;STOMP&lt;/a&gt; - Simple 
Text Orientated Messaging Protocol. It has many similarities with HTTP and is also easy to 
implement. And most messaging middlewares support this protocol and so does Artemis. We already 
configured one acceptor in Artemis especially for the RPG client in the previous post. The 
acronym STOMP hints that the protocol can only be used with text message but that is not the 
case. You can also use STOMP for sending binary data.&lt;/p&gt;
&lt;h2 id=&#34;STOMP-Installation-online&#34;&gt;&lt;a href=&#34;#STOMP-Installation-online&#34; class=&#34;headerlink&#34; title=&#34;STOMP Installation - online&#34;&gt;&lt;/a&gt;STOMP Installation - online&lt;/h2&gt;&lt;p&gt;Luckily there is already an RPG implementation of the STOMP 1.2 protocol ready to use at 
&lt;a href=&#34;https://rpgnextgen.com/&#34;&gt;RPG Next Gen&lt;/a&gt;. It has been conveniently packaged for an easy installation 
with &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg&#34;&gt;iPKG&lt;/a&gt;. We just need to install the iPKG client (just 
download the save file and restore the two objects) and are good to go to install the STOMP 
service program and the corresponding RPG copybooks for all the prototypes.&lt;/p&gt;
&lt;p&gt;iPKG is an ILE program and command and can be executed from the 5250 terminal session.&lt;/p&gt;
&lt;p&gt;First we need to create a library where we want to install the objects. Then we need tell iPKG 
where to look for packages. We do this by registering the RPM repository from RPG Next Gen.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;crtlib ipkg&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ipkg addrepo &amp;#x27;RPGNextGen https://repo.rpgnextgen.com/repository&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;iPKG needs to download and process the meta data from this repository. This is done by executing
the command &lt;code&gt;update&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg update&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The STOMP service program has the following software packages as dependencies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linked List&lt;/li&gt;
&lt;li&gt;Message&lt;/li&gt;
&lt;li&gt;libtree&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All these packages are included in the RPG Next Gen RPM repository and can be installed with iPKG.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install linkedlist&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ipkg install message&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ipkg install libtree&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;After successfully installing the dependencies we can install the STOMP service program.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install stomp&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;For using the STOMP service program we need the prototypes and constants of the STOMP project.
These are provided as copybooks in stream files and are packaged for iPKG available in the RPG 
Next Gen RPM repository. &lt;/p&gt;
&lt;p&gt;Packages which contain stuff necessary for development like prototypes and header files have 
the suffix “&lt;em&gt;-devel&lt;/em&gt;“ like &lt;em&gt;stomp-devel&lt;/em&gt;. By default the stream files will be extracted to the
folder &lt;code&gt;/usr/local/include&lt;/code&gt;. But we can also specify a different location with the parameter
&lt;code&gt;LOC&lt;/code&gt;&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install &amp;#x27;stomp-devel&amp;#x27; loc(&amp;#x27;/home/mihael/include&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now we have everything we need for building our first program with the STOMP service program.&lt;/p&gt;
&lt;h2 id=&#34;STOMP-Installation-offline&#34;&gt;&lt;a href=&#34;#STOMP-Installation-offline&#34; class=&#34;headerlink&#34; title=&#34;STOMP Installation - offline&#34;&gt;&lt;/a&gt;STOMP Installation - offline&lt;/h2&gt;&lt;p&gt;For everybody with an IBM i server with no or restricted access to the internet: &lt;em&gt;iPKG can also&lt;/em&gt;
&lt;em&gt;be used offline&lt;/em&gt;. You will still need to get the RPM packages from the internet (either the 
single packages or the whole repository). But you can do this yourself and put the repository 
or packages on the IFS on your IBM i server or host these packages somewhere inside your network 
on a web server.&lt;/p&gt;
&lt;h3 id=&#34;RPG-Next-Gen-Repository&#34;&gt;&lt;a href=&#34;#RPG-Next-Gen-Repository&#34; class=&#34;headerlink&#34; title=&#34;RPG Next Gen Repository&#34;&gt;&lt;/a&gt;RPG Next Gen Repository&lt;/h3&gt;&lt;p&gt;The RPM repository at &lt;a href=&#34;https://repo.rpgnextgen.com/&#34;&gt;RPG Next Gen&lt;/a&gt; offers the repository packaged
for offline usage. Once you have extracted the repository from the archive file you can use it
with the iPKG command and install packages as usual.&lt;/p&gt;
&lt;p&gt;You first need to register the repository and update the meta data.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg addrepo &amp;#x27;RPGNextGen file:///home/user/repository&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ipkg update&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now you are free to install any packages you need from this repository just by using the package
name on the install command.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install stomp&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;h3 id=&#34;Single-packages&#34;&gt;&lt;a href=&#34;#Single-packages&#34; class=&#34;headerlink&#34; title=&#34;Single packages&#34;&gt;&lt;/a&gt;Single packages&lt;/h3&gt;&lt;p&gt;The other option is to download the packages you are interested in one by one to the IFS and 
install them. The &lt;code&gt;install&lt;/code&gt; command not only accepts a package name but also a RPM file name
and a path to a RPM file. The RPG Next Gen RPM repository lists the available packages in the
&lt;a href=&#34;https://repo.rpgnextgen.com/index.php?content=packages&#34;&gt;RPM Packages&lt;/a&gt; section.&lt;/p&gt;
&lt;p&gt;If you placed the RPM files at &lt;code&gt;/home/user/rpm&lt;/code&gt; and want to install the package STOMP version
2.0.0-2 you can execute the following command.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install &amp;#x27;/home/user/rpm/stomp-2.0.0-2-ppc-ibmi-7.2.rpm&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If you have a couple of packages you want to install by this way you can first change the 
current directory with &lt;code&gt;CHGCURDIR &amp;#39;/home/user/rpm&amp;#39;&lt;/code&gt; and then just state the file name on 
the install command like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install &amp;#x27;stomp-2.0.0-2-ppc-ibmi-7.2.rpm&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;h2 id=&#34;STOMP-Installation-compile&#34;&gt;&lt;a href=&#34;#STOMP-Installation-compile&#34; class=&#34;headerlink&#34; title=&#34;STOMP Installation - compile&#34;&gt;&lt;/a&gt;STOMP Installation - compile&lt;/h2&gt;&lt;p&gt;Of course you can also compile the STOMP project yourself. It is not too hard but requires
some effort as you need to also manually compile the dependencies in the correct version.&lt;/p&gt;
&lt;p&gt;Feel free ;-)&lt;/p&gt;
&lt;h2 id=&#34;iPKG-Notes&#34;&gt;&lt;a href=&#34;#iPKG-Notes&#34; class=&#34;headerlink&#34; title=&#34;iPKG Notes&#34;&gt;&lt;/a&gt;iPKG Notes&lt;/h2&gt;&lt;p&gt;All the shown iPKG commands expect that we use the library &lt;code&gt;IPKG&lt;/code&gt; as our target library. If we 
want to use another library for this we can add the parameter &lt;code&gt;IPKGLIB(MY_TST_LIB)&lt;/code&gt; to &lt;strong&gt;all&lt;/strong&gt;
the &lt;code&gt;ipkg&lt;/code&gt; commands. You can use as many libraries with different repositories and&amp;#x2F;or installed
packages as you like. Each target library is self-contained and does not mess with any other
installation.&lt;/p&gt;
&lt;p&gt;All service programs provided by the RPG Next Gen RPM repository will automatically be added 
to the &lt;code&gt;IPKG&lt;/code&gt; binding directory. The binding directory is located in the library specified in 
the &lt;code&gt;IPKGLIB&lt;/code&gt; parameter.&lt;/p&gt;
&lt;p&gt;When installing packages with objects from the QSYS.LIB file system iPKG will execute a &lt;code&gt;RSTOBJ&lt;/code&gt; 
command. The user installing the packages needs to have enough authorities to use this command.&lt;/p&gt;
&lt;p&gt;For any further infos on iPKG take a look at the &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/wiki/Home&#34;&gt;project wiki&lt;/a&gt;
or contact &lt;a href=&#34;mailto:mihael@rpgnextgen.com&#34;&gt;me&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy integrating!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2023/10/14/2023-10-14%20i%20means%20integration%20-%20part%202/</guid>
            <title>i means integration - Apache Artemis</title>
            <link>http://example.com/2023/10/14/2023-10-14%20i%20means%20integration%20-%20part%202/</link>
            <category>RPG</category>
            <category>IBMi</category>
            <pubDate>Sat, 14 Oct 2023 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;On the previous post I mentioned some pros for decoupling programs. One way of doing this is by
using messages and message queues. One program puts a message on a queue and another program 
receives that message and thus informing the program that it has to do something. There are many 
middleware software packages which support this.&lt;/p&gt;
&lt;p&gt;One of those packages is &lt;a href=&#34;https://activemq.apache.org/components/artemis/&#34;&gt;Apache Artemis&lt;/a&gt;. It 
supports one-to-one connections (queues) but also one-to-many (topics). The good thing about Apache
Artemis is that it is written in Java and thus can run on IBM i. Just download the archive to your
IBM i and extract the content of the archive (f. e. with &lt;code&gt;unzip apache-artemis-2.31.0-bin.zip&lt;/code&gt; or 
&lt;code&gt;jar -xf apache-artemis-2.31.0-bin.zip&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;I like to place the software I manually install on the IFS on IBM i in the &lt;code&gt;/opt&lt;/code&gt; folder. In
addition I like to create a symlink (&lt;code&gt;ln -s ...&lt;/code&gt;) so that it is easier to navigate the folders
(but that is just a personal habit ;-) ). So my folder structure looks like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;/opt/apache-artemis-2.27.1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;/opt/artemis  -&amp;gt;  apache-artemis-2.27.1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Apache Artemis has a very good &lt;a href=&#34;https://activemq.apache.org/components/artemis/documentation/&#34;&gt;documentation&lt;/a&gt;
and they explain everything in detail. Thus here an excerpt from it explaining how to install a
new Apache Artemis broker instance.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The ActiveMQ Artemis broker follows a different paradigm where the project distribution serves 
as the broker “home” and one or more broker “instances” are created which reference the “home” 
for resources (e.g. jar files) which can be safely shared between broker instances. Therefore,
an instance of the broker must be created before it can be run. This may seem like an overhead 
at first glance, but it becomes very practical when updating to a new Artemis version for example.&lt;/p&gt;
&lt;p&gt;To create an Artemis broker instance navigate into the Artemis home folder and run: 
&lt;code&gt;./bin/artemis create /path/to/myBrokerInstance&lt;/code&gt; on the command line.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let us place the Artemis broker instances at &lt;code&gt;/var/local/lib/artemis/&amp;lt;broker&amp;gt;&lt;/code&gt;. The broker will manage
auction messages so my command line looks like&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;./bin/artemis create /var/local/lib/artemis/auction&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now I could run this Artemis instance by executing &lt;code&gt;/var/local/lib/artemis/auction/bin/artemis run&lt;/code&gt;.
But first I need to configure some things. All configuration files of an Artemis instance can be
found at the instance location in the folder &lt;code&gt;etc&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;mihael@ibmi:~/auction-broker/etc&amp;gt; ls -1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;artemis-roles.properties&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;artemis-users.properties&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;artemis.profile&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;bootstrap.xml&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;broker.xml&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;jolokia-access.xml&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;log4j2.properties&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;login.config&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;management.xml&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;Acceptors&#34;&gt;&lt;a href=&#34;#Acceptors&#34; class=&#34;headerlink&#34; title=&#34;Acceptors&#34;&gt;&lt;/a&gt;Acceptors&lt;/h2&gt;&lt;p&gt;Acceptors are the things that make the network connections to the clients and receive&amp;#x2F;distribute
messages. Artemis supports many protocols. But the protocol we are interested in is STOMP. Artemis
can be configured to have many acceptors listening on different ports but also having one acceptor
accepting multiple protocols listening on one port.&lt;/p&gt;
&lt;p&gt;Note: As I need some “special” connection attributes for the local STOMP acceptor I go for one 
      acceptor for local STOMP connections and one acceptor for everything else.&lt;/p&gt;
&lt;p&gt;Here is the acceptor configuration in &lt;code&gt;broker.xml&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight xml&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;acceptors&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;&amp;lt;!-- Acceptor for every supported protocol available locally and on the network --&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;acceptor&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;name&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;artemis&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;tcp://0.0.0.0:40001?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;amqpMinLargeMessageSize=102400;protocols=CORE,AMQP,STOMP,HORNETQ,MQTT,OPENWIRE;useEpoll=true;amqpCredits=1000;amqpLowCredits=300;amqpDuplicateDetection=true;supportAdvisory=false;suppressInternalManagementObjects=false&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;acceptor&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;&amp;lt;!-- &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;        STOMP acceptor only available on localhost with extra long connection timeout. &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;        connection TTL: 60 * 60 * 24 * 1000 = 86400000 milliseconds = one day&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;    --&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;acceptor&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;name&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;stomp&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;tcp://localhost:40002?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=STOMP;useEpoll=true;connectionTtl=86400000&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;acceptor&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;acceptors&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Note: The &lt;a href=&#34;https://activemq.apache.org/components/artemis/documentation/latest/stomp.html#stomp-heart-beating-and-connection-ttl&#34;&gt;connection TTL attribute&lt;/a&gt; 
      is set because the RPG STOMP service program does not support sending heart beats at the 
      moment and Apache Artemis does not accept having no heart beat (and I wouldn’t either ;-) ) 
      so we are setting a very high connection TTL so that it is guaranteed that some event will 
      be sent in this time frame (f. e. a day or a week).&lt;/p&gt;
&lt;h2 id=&#34;Topic&#34;&gt;&lt;a href=&#34;#Topic&#34; class=&#34;headerlink&#34; title=&#34;Topic&#34;&gt;&lt;/a&gt;Topic&lt;/h2&gt;&lt;p&gt;Queues and topics can be automatically created but I also can specify a persistent topic in the
configuration.&lt;/p&gt;
&lt;figure class=&#34;highlight xml&#34;&gt;&lt;figcaption&gt;&lt;span&gt;broker.xml&lt;/span&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;addresses&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;address&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;name&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;auction-events&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;multicast&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;queue&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;name&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;sink&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;durable&lt;/span&gt;&amp;gt;&lt;/span&gt;true&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;durable&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;queue&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;multicast&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;address&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;addresses&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Here we have a topic &lt;code&gt;auction-events&lt;/code&gt; and even if there is no subscriber to that topic all
messages will at least be distributed to the queue &lt;code&gt;sink&lt;/code&gt; which is really convenient in the 
development phase. So even if nobody subscribed to &lt;code&gt;auction-events&lt;/code&gt; we will still see the incoming
messages in the queue &lt;code&gt;sink&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;Logging&#34;&gt;&lt;a href=&#34;#Logging&#34; class=&#34;headerlink&#34; title=&#34;Logging&#34;&gt;&lt;/a&gt;Logging&lt;/h2&gt;&lt;p&gt;From the documentation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Apache ActiveMQ Artemis uses the SLF4J logging facade for logging, with the broker assembly 
providing &lt;a href=&#34;https://logging.apache.org/log4j/2.x/&#34;&gt;Log4J2&lt;/a&gt; as the logging implementation. This 
is configurable via the &lt;code&gt;log4j2.properties&lt;/code&gt; file found in the broker instance etc directory, 
which is configured by default to log to both the console and to a file.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I couldn’t have written it better.&lt;/p&gt;
&lt;p&gt;For logging everything related to the STOMP acceptor just raise the log level of the 
&lt;code&gt;StompConnection&lt;/code&gt; logger.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;logger.org.apache.activemq.artemis.core.protocol.stomp.StompConnection.level=DEBUG&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Note: You may need to also raise the level of the corresponding appender to &lt;code&gt;DEBUG&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;appender.console.filter.threshold.type = ThresholdFilter&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;appender.console.filter.threshold.level = DEBUG&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;h2 id=&#34;Web-Console&#34;&gt;&lt;a href=&#34;#Web-Console&#34; class=&#34;headerlink&#34; title=&#34;Web Console&#34;&gt;&lt;/a&gt;Web Console&lt;/h2&gt;&lt;p&gt;Apache Artemis comes out-of-the-box with a web console based on &lt;a href=&#34;https://hawt.io/&#34;&gt;Hawtio&lt;/a&gt;.
The only problem with this is that it is only available on the local machine. But just for 
giving this a try it would be easier if we could access it from outside localhost. For this we 
need to change two configuration entries.&lt;/p&gt;
&lt;figure class=&#34;highlight xml&#34;&gt;&lt;figcaption&gt;&lt;span&gt;bootstrap.xml&lt;/span&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;web&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;...&lt;/span&gt; &amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;binding&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;name&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;artemis&amp;quot;&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;uri&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;http://0.0.0.0:40000&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;binding&lt;/span&gt;&amp;gt;&lt;/span&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;web&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;figure class=&#34;highlight xml&#34;&gt;&lt;figcaption&gt;&lt;span&gt;jolokia-access.xml&lt;/span&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;cors&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;allow-origin&lt;/span&gt;&amp;gt;&lt;/span&gt;*&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;allow-origin&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  ...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;cors&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now we can access the Apache Artemis web console running on IBM i from our computer by accessing
the URL &lt;code&gt;http://my_ibmi:40000&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;Large-Text-Messages&#34;&gt;&lt;a href=&#34;#Large-Text-Messages&#34; class=&#34;headerlink&#34; title=&#34;Large Text Messages&#34;&gt;&lt;/a&gt;Large Text Messages&lt;/h2&gt;&lt;p&gt;By default the web console will show you only the first 256 characters from a message. Depending
on the project messages might be much larger (several thousand characters). The max. number of 
characters shown in the web console can be set in the &lt;code&gt;broker.xml&lt;/code&gt; file.&lt;/p&gt;
&lt;figure class=&#34;highlight xml&#34;&gt;&lt;figcaption&gt;&lt;span&gt;broker.xml&lt;/span&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;address-settings&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;address-setting&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;management-message-attribute-size-limit&lt;/span&gt;&amp;gt;&lt;/span&gt;50000&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;management-message-attribute-size-limit&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;address-setting&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;address-settings&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Here the max. number of displayed characters is set to 50.000.&lt;/p&gt;
&lt;h3 id=&#34;Starting-Artemis&#34;&gt;&lt;a href=&#34;#Starting-Artemis&#34; class=&#34;headerlink&#34; title=&#34;Starting Artemis&#34;&gt;&lt;/a&gt;Starting Artemis&lt;/h3&gt;&lt;p&gt;Now I can run this Artemis instance by executing &lt;code&gt;/var/local/lib/artemis/auction/bin/artemis run&lt;/code&gt;
and have the log output in &lt;code&gt;/var/local/lib/artemis/auction/log/artemis.log&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But there are other ways to start Artemis, f. e. via &lt;a href=&#34;https://github.com/ThePrez/ServiceCommander-IBMi&#34;&gt;Service Commander&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&#34;SocketException-on-Startup&#34;&gt;&lt;a href=&#34;#SocketException-on-Startup&#34; class=&#34;headerlink&#34; title=&#34;SocketException on Startup&#34;&gt;&lt;/a&gt;SocketException on Startup&lt;/h4&gt;&lt;p&gt;Depending on the network configuration of the IBM i server we may encounter the following error on
startup:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;java.net.SocketException: Protocol driver not attached&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;This error may occur on any product&amp;#x2F;project which utilizes Jetty. But we can work around this
problem by specifying a Java system property. The startup script of Artemis supports adding Java 
system properties without having to modify the script by using the environment variable
&lt;code&gt;JAVA_ARGS_APPEND&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;export JAVA_ARGS_APPEND=&amp;quot;-Djava.net.preferIPv4Stack=true&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now we can start Artemis as usual.&lt;/p&gt;
&lt;h3 id=&#34;Service-Commander&#34;&gt;&lt;a href=&#34;#Service-Commander&#34; class=&#34;headerlink&#34; title=&#34;Service Commander&#34;&gt;&lt;/a&gt;Service Commander&lt;/h3&gt;&lt;p&gt;Service Commander is a tool for managing services and applications on IBM i. But in contrast
to other tools Service Commander can manage services and applications of both worlds, PASE and 
native IBM i, and has a very nice integration with the native CLI by utilizing &lt;code&gt;STRTCPSVR&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Service Commander stores the necessary data for each managed service in a stream file on the IFS,
see folder &lt;code&gt;.sc/services&lt;/code&gt; in the home folder. I placed the following configuration at 
&lt;code&gt;/home/myuser/.sc/services/artemis.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;name: Artemis&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dir: /var/local/lib/artemis/auction&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;start_cmd: /var/local/lib/artemis/auction/bin/artemis run&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;stop_cmd: /var/local/lib/artemis/auction/bin/artemis stop&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;check_alive: 40000,40001,40002&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;batch_mode: yes&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;sbmjob_jobname: ARTEMIS&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;sbmjob_opts: JOBQ(QUSRNOMAX) USER(PM)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;environment_vars:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  - &amp;quot;JAVA_HOME=/QOpenSys/pkgs/lib/jvm/openjdk-11&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  - &amp;quot;QIBM_MULTI_THREADED=Y&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  - &amp;quot;JAVA_ARGS_APPEND=-Djava.net.preferIPv4Stack=true&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now we have a whole bunch of possibilities for starting our Apache Artemis instance.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;From a SSH session: &lt;code&gt;nohup /var/local/lib/artemis/auction/bin/artemis run &amp;gt; /dev/null 2&amp;gt; /dev/null &amp;amp;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;From a SSH session: &lt;code&gt;sc start artemis&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;From 5250 session: &lt;code&gt;STRTCPSVR SERVICE(*SC) INSTANCE(&amp;#39;artemis&amp;#39;)&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And now we can start it equally well from the PASE and the 5250 environment. And also no problems 
with having it autostarted after an IPL. Just add it to the &lt;em&gt;autostart&lt;/em&gt; group in the Service Commander
job definition YAML file.&lt;/p&gt;
&lt;p&gt;Kudos to Jesse Gorzinski for this great project!&lt;/p&gt;
&lt;p&gt;Happy integrating!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
&lt;p&gt;PS: I mentioned “… written in Java … thus can run on IBM i.”. That is not totally correct.
    Because it is written in Java it can run in the Java VM on IBM i. But that does not guarantee
    that it will run correctly on IBM i. Many Java projects are done in mind with x86 as their
    target platform (or even only Windows) and thus it does not always work on IBM i. But so far 
    I had luck with all of the Java projects I tried on IBM i. :-)&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2023/10/11/2023-10-11%20i%20means%20integration%20-%20part%201/</guid>
            <title>i means integration - part 1</title>
            <link>http://example.com/2023/10/11/2023-10-11%20i%20means%20integration%20-%20part%201/</link>
            <category>RPG</category>
            <category>IBMi</category>
            <pubDate>Wed, 11 Oct 2023 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;As many know I am a proponent of the platform IBM i. Many see it as an old legacy platform which
cannot play in the modern world of computing. Nothing could be farther from the truth. It is one
of the most advanced platforms out there.&lt;/p&gt;
&lt;p&gt;And what about the programming languages which has been with the platform since its beginnings?
RPG has evolved into a modern and one of best programming languages for business logic. Because
of its ILE capabilities it has access to the whole ILE environment including ILE C and C++ modules
and libraries. And thus it can play in any game it needs to play … also in the web and micro 
service game of today.&lt;/p&gt;
&lt;p&gt;But first let us have a look at the following case.&lt;/p&gt;
&lt;h3 id=&#34;The-Case&#34;&gt;&lt;a href=&#34;#The-Case&#34; class=&#34;headerlink&#34; title=&#34;The Case&#34;&gt;&lt;/a&gt;The Case&lt;/h3&gt;&lt;p&gt;We have an online auction house for our famous online game. The game started decades ago where we
only had to cover ingame trades. We have a program for managing these trades: ACT001. It covers 
all the necessary attributes of an ingame auction.&lt;/p&gt;
&lt;p&gt;And then … things started to evolve. Other media channels (like WhatsApp, Discord or Telegram) 
sprang up and we want to take advantage of these new possibilities. We like to inform players about
new auctions of interest and so we need to provide the interface to these media channels with data. 
There is also a player by the name of “extranet” which needs some of the data.&lt;/p&gt;
&lt;p&gt;So we have more information to manage which might evolve into multiple programs for managing data
and we have different channels where data enters our system. Additionally we have multiple systems
and interfaces which need to be provisioned with our data.&lt;/p&gt;
&lt;p&gt;After some years most RPG programs look like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;begsr savact;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  ...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  write actdtaf01 data;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  call act002; // WhatsApp&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  call act003; // Telegram&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  call act004; // Extranet&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  ...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;endsr;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;And the situation is more like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/images/i%20means%20integration%20part%201%20-%20unstructured.png&#34; alt=&#34;Unstructured program calls&#34;&gt;&lt;/p&gt;
&lt;p&gt;Every auction data management program needs to call every distribution program which leads to a
maintenance nightmare. One solution would be to decouple things.&lt;/p&gt;
&lt;h3 id=&#34;Decoupling-Pros-and-Cons&#34;&gt;&lt;a href=&#34;#Decoupling-Pros-and-Cons&#34; class=&#34;headerlink&#34; title=&#34;Decoupling : Pros and Cons&#34;&gt;&lt;/a&gt;Decoupling : Pros and Cons&lt;/h3&gt;&lt;p&gt;Pros for decoupling components (programs):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;defined interface between components&lt;/li&gt;
&lt;li&gt;easier to extend&lt;/li&gt;
&lt;li&gt;easier to maintain&lt;/li&gt;
&lt;li&gt;easier to scale&lt;/li&gt;
&lt;li&gt;easier to handle errors (and replay events)&lt;/li&gt;
&lt;li&gt;… and some more&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Of course like with everything else in life there are not only pros but also cons like &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;you cannot derive from the management program where the data will flow to&lt;/li&gt;
&lt;li&gt;some events must be processed in the correct order&lt;/li&gt;
&lt;li&gt;may need more infrastructure (software)&lt;/li&gt;
&lt;li&gt;… and probably some more&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But in my opinion the pros outweight the cons big time.&lt;/p&gt;
&lt;h3 id=&#34;Solution&#34;&gt;&lt;a href=&#34;#Solution&#34; class=&#34;headerlink&#34; title=&#34;Solution&#34;&gt;&lt;/a&gt;Solution&lt;/h3&gt;&lt;p&gt;Some may say:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;That is pretty easy. Put the calling of the distribution program in a trigger.
It will be executed every time anything changes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yes. That may be a solution. But in case a distribution program is called and the call results 
in an error what will happen to the update of the data? Will it be rolled back? But there is
nothing wrong with the data. Perhaps the receiving system is not online. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Why should we not update our own system and move on?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We could do that but then what about the update of the other system that needs our data?
Now we need a way to handle the error case(s) … for every distribution program.&lt;/p&gt;
&lt;p&gt;So a trigger may be one viable solution … but there are also others solutions.&lt;/p&gt;
&lt;h3 id=&#34;Another-solution&#34;&gt;&lt;a href=&#34;#Another-solution&#34; class=&#34;headerlink&#34; title=&#34;Another solution&#34;&gt;&lt;/a&gt;Another solution&lt;/h3&gt;&lt;p&gt;If we distribute our data to other systems it may not be needed there immediately and may not 
always need to be in sync with our own system. So most of the time we may be able to do the 
distribution asynchronously.&lt;/p&gt;
&lt;p&gt;Synchronously : We tell someone that our data has changed and watch that someone do his work with our data to update his system.&lt;/p&gt;
&lt;p&gt;Asynchronously with one receiver: We tell someone that our data has changed and he should update his system. We don’t wait and watch his update but just move on and do our work.&lt;/p&gt;
&lt;p&gt;Asynchronously with multiple receiver: We tell someone that our data has changed and that he should inform everybody who is interested in the data change. And we just move on.&lt;/p&gt;
&lt;p&gt;The one-to-one situation of data publisher and data receiver can already be done with little effort natively on IBM i.&lt;/p&gt;
&lt;p&gt;The magic keywords are: data queues!&lt;/p&gt;
&lt;p&gt;They are easy to manage and use and are very performant. But in our case they are of little help
as we have many different data receivers.&lt;/p&gt;
&lt;p&gt;For the 1-to-n situation we can use a concept called &lt;a href=&#34;https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern&#34;&gt;pub&amp;#x2F;sub&lt;/a&gt;. One publisher publishes
an event and multiple subscribers may subscribe on this type of event. This concept is quite
generic and may be used pretty much everywhere. Frontend, backend … it doesn’t matter.&lt;/p&gt;
&lt;p&gt;Does this solution rules out the use of a database trigger: no. Acutally a trigger might be a good
way to trigger this event and act as the data&amp;#x2F;event publisher.&lt;/p&gt;
&lt;p&gt;There is currently no ILE solution for pub&amp;#x2F;sub that I know of. But that does not matter so much as
we want to do things in an asynchronous way anyway (pretty much fire and forget) and thus it does 
not matter if it is ILE or not. The only thing which should be ILE compatible is the data publisher. 
And we got that covered with an open source service program available on &lt;a href=&#34;https://www.rpgnextgen.com/&#34;&gt;rpgnextgen.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are many systems available which cover the pub&amp;#x2F;sub concept. A very famous, stable and widely
used system is part of the Apache software ecosystem: &lt;a href=&#34;https://activemq.apache.org/components/artemis/&#34;&gt;Apache Artemis&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A good thing about Apache Artemis is that it is written in Java and thus can run on IBM i without
any problems.&lt;/p&gt;
&lt;p&gt;In the next episode we take a look at Apache Artemis and how to run it on our beloved IBM i.&lt;/p&gt;
&lt;p&gt;Happy integrating!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2023/02/12/2023-02-12%20Jenkins%20on%20i%20-%20Part%201/</guid>
            <title>Jenkins on IBM i - Part 1</title>
            <link>http://example.com/2023/02/12/2023-02-12%20Jenkins%20on%20i%20-%20Part%201/</link>
            <category>CI</category>
            <category>RPG</category>
            <category>IBMi</category>
            <pubDate>Sun, 12 Feb 2023 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;Some time ago (probably some years) I wrote a wiki article “IBM i and Continuous Integration”.
I was kind of successful with my adventure in the realm of CI and DevOps to some degree. 
I could install the CI server but had some trouble with running the commands on IBM i.&lt;/p&gt;
&lt;p&gt;Some years and software versions later I am giving it a try again. I think CI is an important
part in software development and not so hard to implement (at least when it comes to the basics).&lt;/p&gt;
&lt;p&gt;So what is the goal?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Continuous Integration!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But what is “Continuous Integration”? Again I will refer to the Wikipedia article 
&lt;a href=&#34;https://en.wikipedia.org/wiki/Continuous_integration&#34;&gt;Continous integration&lt;/a&gt;. 
I think the main point is this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;… the practice of merging all developers’ working copies to a shared mainline
several times a day that triggers automated build with testing …&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And today this merging can be done even easier with the version control systems
of today which can trigger a build automatically after a successful merge.&lt;/p&gt;
&lt;h2 id=&#34;Why-CI-even-if-you-are-a-single-developer-in-your-shop&#34;&gt;&lt;a href=&#34;#Why-CI-even-if-you-are-a-single-developer-in-your-shop&#34; class=&#34;headerlink&#34; title=&#34;Why CI even if you are a single developer in your shop?&#34;&gt;&lt;/a&gt;Why CI even if you are a single developer in your shop?&lt;/h2&gt;&lt;p&gt;Having a build server can be handy for many reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build server can be setup to have the same environment as the production environment. The development environment often differs from the production environment.&lt;/li&gt;
&lt;li&gt;Every build is done in exactly the same way.&lt;/li&gt;
&lt;li&gt;Builds can be triggered regardless of the developer machine.&lt;/li&gt;
&lt;li&gt;Release builds&lt;/li&gt;
&lt;li&gt;… and there are many many more&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here some quotes from the net:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The CI build can also be thought of as your “release” build. The environment should be stable, 
and unaffected by whatever development gizmo you just add to your machine. It should allow you 
to always reproduce a build. This can be valuable if you add a new dependency to your project, 
and forget to setup the release build environment to take that into account.
&amp;mdash; &lt;a href=&#34;http://stackoverflow.com/a/1143325&#34;&gt;Is Continuous Integration important for a solo developer?&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Likewise, if you are building libs that are used by multiple projects then CI will make sure 
they work with ALL of the projects rather than just the one that you’re working with right now…
&amp;mdash; &lt;a href=&#34;http://stackoverflow.com/a/1143325&#34;&gt;Is Continuous Integration important for a solo developer?&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So … Continuous integration is for everyone!&lt;/p&gt;
&lt;h2 id=&#34;Task&#34;&gt;&lt;a href=&#34;#Task&#34; class=&#34;headerlink&#34; title=&#34;Task&#34;&gt;&lt;/a&gt;Task&lt;/h2&gt;&lt;p&gt;We will take a RPG project with the source located in a Git repository and will build it with
Jenkins on an IBM i server. The build will be executed with the tool &lt;code&gt;make&lt;/code&gt;. We will see what
Jenkins and its’ plugins can bring to the table.&lt;/p&gt;
&lt;h2 id=&#34;Build-Server&#34;&gt;&lt;a href=&#34;#Build-Server&#34; class=&#34;headerlink&#34; title=&#34;Build Server&#34;&gt;&lt;/a&gt;Build Server&lt;/h2&gt;&lt;p&gt;Nothing has changed here. I am still a big fan of &lt;a href=&#34;https://www.jenkins.io/&#34;&gt;Jenkins&lt;/a&gt;. Jenkins is
written in Java and runs very well on IBM i without any modifications. You should install Java 11 
on your IBM i either via yum or as a licensed product.&lt;/p&gt;
&lt;p&gt;We can test the java installation with executing &lt;code&gt;java -version&lt;/code&gt; on the shell. Your output in a 
shell should look something like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;-bash-5.1$ java -version&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;openjdk version &amp;quot;11.0.11-ea&amp;quot; 2021-04-20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;OpenJDK Runtime Environment (build 11.0.11-ea+9)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;Eclipse OpenJ9 VM (build openj9-0.26.0, JRE 11 OS400 ppc64-64-Bit Compressed References 20220208_964 (JIT enabled, AOT enabled)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;OpenJ9   - b4cc246&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;OMR      - 162e6f7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;JCL      - 7796c80 based on jdk-11.0.11+9)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The important part is the version “11.0.11-ea”. It should at least state version 11. Java in general
is very backwards compatible. A more recent version than 11 should work without any problems.&lt;/p&gt;
&lt;h2 id=&#34;Installation&#34;&gt;&lt;a href=&#34;#Installation&#34; class=&#34;headerlink&#34; title=&#34;Installation&#34;&gt;&lt;/a&gt;Installation&lt;/h2&gt;&lt;p&gt;Jenkins can be downloaded from the &lt;a href=&#34;https://www.jenkins.io/&#34;&gt;Jenkins&lt;/a&gt; website.&lt;/p&gt;
&lt;p&gt;The installation is pretty straight forward. Jenkins is packaged as a war file but also supports
executing the file as a normal Java application. &lt;/p&gt;
&lt;p&gt;The easiest way to start Jenkins is from a shell (f. e. bash via a SSH connection):&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;java -jar jenkins.war&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The Jenkins project has an extensive documentation. The website provides a page 
&lt;a href=&#34;https://www.jenkins.io/doc/book/installing/&#34;&gt;Installing Jenkins&lt;/a&gt;. The 
&lt;a href=&#34;https://www.jenkins.io/doc/book/installing/war-file/&#34;&gt;WAR file section&lt;/a&gt; is probably the
documented installation platform which maps best to IBM i PASE (AIX).&lt;/p&gt;
&lt;p&gt;The Jenkins runtime can be configured via command line parameters and Java system properties. 
A list of supported command line parameters (f. e. HTTP port) can also be found 
&lt;a href=&#34;https://www.jenkins.io/doc/book/installing/initial-settings/&#34;&gt;here&lt;/a&gt;. The extensive list of
Java system properties can be found &lt;a href=&#34;https://www.jenkins.io/doc/book/managing/system-properties/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;By default all the data will be stored in the &lt;code&gt;.jenkins&lt;/code&gt; folder in the home directory of the 
current user and the default port is 8080.&lt;/p&gt;
&lt;p&gt;This might not be convenient for your environment and we can change that to some other values.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;export JENKINS_HOME=/var/local/lib/jenkins&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;java -jar jenkins.war --httpPort 5000&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now Jenkins uses port 5000 and stores its data in the folder &lt;code&gt;/var/local/lib/jenkins&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Note: You may need to add the Java system property “java.net.preferIPv4Stack&amp;#x3D;true” when you
      see Jetty choking out some weird network exception. So your command line then looks like
      &lt;code&gt;java -Djava.net.preferIPv4Stack=true -jar jenkins.war --httpPort=5000&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;Configuration&#34;&gt;&lt;a href=&#34;#Configuration&#34; class=&#34;headerlink&#34; title=&#34;Configuration&#34;&gt;&lt;/a&gt;Configuration&lt;/h2&gt;&lt;p&gt;The path to the tools used in the build jobs need to either be specified in Jenkins or need to
be in the PATH environment variable.&lt;/p&gt;
&lt;p&gt;The PATH environment variable can be configured in Jenkins management section.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;Dashboard ⇒ Manage Jenkins ⇒ Configure System ⇒ Global Properties&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The location of some essential tools can be configured extra in&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;Dashboard ⇒ Manage Jenkins ⇒ Global Tool Configuration&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If you don’t have &lt;code&gt;git&lt;/code&gt; in the PATH you can set it here to &lt;code&gt;/QOpenSys/pkgs/bin/git&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;Build-Job&#34;&gt;&lt;a href=&#34;#Build-Job&#34; class=&#34;headerlink&#34; title=&#34;Build Job&#34;&gt;&lt;/a&gt;Build Job&lt;/h2&gt;&lt;p&gt;A Build Job defines what is executed and also where and when. It defines the execution environment.
In Jenkins you can configure a job in different ways. I will cover “Freestyle Project” and “Pipeline”.
Freestyle projects consist of predefined build steps which can be concated to a workflow. Pipeline
projects are mainly defined by a pipeline script which is written in a specific DSL. Pipeline scripts
can be written in two different styles: scripted and declarative. In the declarative style you use 
mainly predefined functions and don’t script as much yourself (though you also have the option to 
script in the declarative pipeline). The Pipeline project will be covered in more detail in another
post. Here I will focus on the “Freestyle” project.&lt;/p&gt;
&lt;h3 id=&#34;Freestyle-Project&#34;&gt;&lt;a href=&#34;#Freestyle-Project&#34; class=&#34;headerlink&#34; title=&#34;Freestyle Project&#34;&gt;&lt;/a&gt;Freestyle Project&lt;/h3&gt;&lt;p&gt;Build Jobs can be configured in Jenkins in &lt;code&gt;Dashboard ⇒ New Item&lt;/code&gt;. Configure the job as a 
“Freestyle Project”. Freestyle projects are configured in the UI with build steps and configuration 
options contributed by other Jenkin plugins.&lt;/p&gt;
&lt;p&gt;The workflow of a “freestyle” build consists of steps. Steps can either be added as “Build Steps” or 
“Post-Build Steps” by using the corresponding dropdown button in the configuration of the build job.&lt;/p&gt;
&lt;h4 id=&#34;Git&#34;&gt;&lt;a href=&#34;#Git&#34; class=&#34;headerlink&#34; title=&#34;Git&#34;&gt;&lt;/a&gt;Git&lt;/h4&gt;&lt;p&gt;In a freestyle project there is a predefined option to check out the source code from a source code
repository. Here we can enter our URL for the Git repository.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/images/jenkins-freestyle-git.png&#34; alt=&#34;Jenkins Git Repository Setting&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;Compile&#34;&gt;&lt;a href=&#34;#Compile&#34; class=&#34;headerlink&#34; title=&#34;Compile&#34;&gt;&lt;/a&gt;Compile&lt;/h4&gt;&lt;p&gt;This project uses &lt;code&gt;make&lt;/code&gt; for building the project. We first execute the &lt;code&gt;purge&lt;/code&gt; target to have
a clean build environment and then the &lt;code&gt;all&lt;/code&gt; target. We specify the target library as a parameter
of the gmake command, see &lt;code&gt;BIN_LIB&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;gmake BIN_LIB=MIHAELSTMP purge all&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Note: I am using &lt;code&gt;gmake&lt;/code&gt; which can be installed via &lt;code&gt;yum&lt;/code&gt; which is the GNU version of &lt;code&gt;make&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We add a build step “Execute shell”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/images/jenkins-freestyle-compile.png&#34; alt=&#34;Jenkins Compile Build Step&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What happens on a failed compile?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Easy. &lt;code&gt;gmake&lt;/code&gt; will return a non zero exit value if it cannot execute a target successfully. By
default the shell step will mark the build as failed when it gets a non zero exit value from
the executed shell command. So on a failed compile the build will automatically be marked as 
failed.&lt;/p&gt;
&lt;h2 id=&#34;Open-Tasks&#34;&gt;&lt;a href=&#34;#Open-Tasks&#34; class=&#34;headerlink&#34; title=&#34;Open Tasks&#34;&gt;&lt;/a&gt;Open Tasks&lt;/h2&gt;&lt;p&gt;In the old wiki article I mentioned the Task Scanner Plugin for scanning the source code for 
specific keywords which mark open tasks. That was a couple of years ago. In the meantime many 
plugins have been merged into the 
&lt;a href=&#34;https://github.com/jenkinsci/warnings-ng-plugin/blob/master/doc/Documentation.md&#34;&gt;Warnings Next Generation&lt;/a&gt; plugin which also now includes the feature set of the Task Scanner plugin.&lt;/p&gt;
&lt;p&gt;In a freestyle project the open task scanning can be added as a post-build action by selecting
“Record compiler warnings and static analysis results”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/images/jenkins-freestyle-opentasks.png&#34; alt=&#34;Jenkins Open Tasks&#34;&gt;&lt;/p&gt;
&lt;p&gt;The result can be found by selecting the menu entry “Open Tasks” on the main page of the 
executed build.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/images/jenkins-freestyle-opentasks-result.png&#34; alt=&#34;Jenkins Open Tasks Result&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;Notification&#34;&gt;&lt;a href=&#34;#Notification&#34; class=&#34;headerlink&#34; title=&#34;Notification&#34;&gt;&lt;/a&gt;Notification&lt;/h2&gt;&lt;p&gt;Having an automated build is pretty cool. But what about the results. In most cases we would want 
to know the result of the build. But we don’t want to stare at the screen and wait for the result.
We want to be informed by Jenkins when the build has finished. &lt;/p&gt;
&lt;p&gt;Jenkins has many plugins which cover the different ways of communicating the result of a build. 
But the most simple way is to get an email. We can use the default email plugin or use the extended
email plugin. Both plugins can be configured under &lt;code&gt;Dashboard ⇒ Manage Jenkins ⇒ Configure System&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But just installing and configuring the email plugin is not enough. That will not trigger an email
on a finished build. We need to add a Post-Build action step like “E-mail Notification” or 
“Editable Email Notification”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/images/jenkins-freestyle-email.png&#34; alt=&#34;Jenkins Email Notification&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;Starting-Jenkins&#34;&gt;&lt;a href=&#34;#Starting-Jenkins&#34; class=&#34;headerlink&#34; title=&#34;Starting Jenkins&#34;&gt;&lt;/a&gt;Starting Jenkins&lt;/h2&gt;&lt;p&gt;At the end I would like to introduce you to another option of starting Jenkins on IBM i. The
project &lt;a href=&#34;https://github.com/ThePrez/ServiceCommander-IBMi&#34;&gt;Service Commander&lt;/a&gt; from Jesse 
Gorzinski does a wonderful job of managing services and keeps configuring the environment of 
those services simple. Especially the Java and Node.js applications make heavy use of 
environment variables and that is one area where Service Commander really shines.&lt;/p&gt;
&lt;p&gt;I don’t want to go into the details of how to install and use Service Commander because Jesse
already did a great job of that on the project Github site. Just read the README file. But I 
want to share my start script with you.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;name: Jenkins&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dir: /home/mihael/jenkins&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;start_cmd: java -Djava.net.preferIPv4Stack=true -jar jenkins.war --httpPort=5000&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;check_alive: 5000&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;batch_mode: true&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;sbmjob_jobname: JENKINS&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;sbmjob_opts: JOBQ(QUSRNOMAX)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;environment_vars:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;- HOME=/home/mihael&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;- JAVA_HOME=/QOpenSys/pkgs/lib/jvm/openjdk-11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;- PATH=/QOpenSys/pkgs/bin:/QOpenSys/usr/bin:/usr/ccs/bin:/QOpenSys/usr/bin/X11:/usr/sbin:.:/usr/bin&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;The beauty of this is that you can define your environment without any hassle and still integrate
with the traditional management of jobs on the IBM i server. This Jenkins instance will have 
&lt;em&gt;JENKINS&lt;/em&gt; as its job name and thus can be easily found. &lt;/p&gt;
&lt;p&gt;Kudos to Jesse!&lt;/p&gt;
&lt;h2 id=&#34;Wrap-up&#34;&gt;&lt;a href=&#34;#Wrap-up&#34; class=&#34;headerlink&#34; title=&#34;Wrap up&#34;&gt;&lt;/a&gt;Wrap up&lt;/h2&gt;&lt;p&gt;With a couple of steps we can have quite a smooth development lifecycle which automates many of
those tedious steps and let us play it safe with source code scanning and unit testing … 
in a team or as a lone wolf.&lt;/p&gt;
&lt;p&gt;There are still some areas which we haven’t covered in detail: unit testing and documentation.
Perhaps this will be covered in a future post.&lt;/p&gt;
&lt;p&gt;Jenkins also supports triggering a build by a HTTP request. But as this post is already very 
lengthy this topic may be covered in another post.&lt;/p&gt;
&lt;p&gt;Happy building!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2022/11/18/2022-11-17-cool-rpg/</guid>
            <title>Cool things you might not know about RPG or ILE</title>
            <link>http://example.com/2022/11/18/2022-11-17-cool-rpg/</link>
            <category>RPG</category>
            <pubDate>Fri, 18 Nov 2022 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;Long time no see! :) . It has been a while since my last post. Here is a little different post.
I compiled a list of things I really like a lot about RPG and the ILE environment and am missing
(most of the time) in other programming languages &amp;#x2F; environments.&lt;/p&gt;
&lt;p&gt;Note: The features in the list are in no special order.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Database connections &amp;mdash; This one is a real no brainer. I have encountered no other programming
language or environment where it is so easy to get access to the database as on IBM i and with RPG. 
No connection pooling, no connection management. Just “always on” :) !&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Database operations &amp;mdash; This is also about the database but more about how to work with &amp;#x2F; access
the database from RPG and not about the connection. In RPG we can choose between the built-in support
for Record-Level-Access and embedded SQL. No extra library to load. Just working out of the box.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data types &amp;mdash; RPG has a rich collection of data types. Integers and chars are covered by
every programming language but you probably won’t find a numeric data type with fixed decimal 
positions. Date, time and timestamp are also very nice to have and especially the operations you 
can do with them in RPG.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pointers &amp;mdash; Not widely used in RPG but pointer support is really nice. Especially the
feature that you can declare a variable based on a pointer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://www.ibm.com/docs/en/i/7.4?topic=heap-user-created-heaps&#34;&gt;User heaps&lt;/a&gt; &amp;mdash; User defined memory heaps are really cool when you have to deal
a lot with dynamic memory management. You don’t necessarily have to keep track of all the allocated 
memory. Just discard the heap and everything is cleaned up for you by the system. No keeping 
track of pointers. Another cool feature is that you can &lt;a href=&#34;https://www.ibm.com/docs/en/i/7.4?topic=ssw_ibm_i_74/apis/CEEMKHP.html&#34;&gt;mark&lt;/a&gt; a memory allocation 
in the heap and later free up everything up to that mark.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;User spaces &amp;mdash; User spaces also exist on other platforms but I think they don’t have so
many features there. On IBM i user spaces are real objects on the file system with all 
features of an object in the QSYS.LIB file system. They are also persistent between reboots.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data areas &amp;mdash; These easy to use objects are great for storing simple data. They have
native support for editing and displaying the data and are fully integrated into the RPG 
programming language. On other platforms you would probably use a normal text file. But
reading, locking and writing a text file on other platforms is not as easy as in RPG.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CCSID conversion &amp;mdash; Another great underrated feature is the CCSID support in RPG. You
can declare a character variable with any CCSID and assign a value to it. This value will
be automatically converted to the CCSID of the declared variable. No extra step needed. So
for converting a value with EBCDIC CCSID 37 to UTF-8 is only declaration of two variables
and the assignment of the value. That’s it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Compile time data &amp;mdash; This is usually used to initialize arrays. It can easily be
achieved by other means in other programming languages but I found a liking to how it is 
done in RPG.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://www.techtarget.com/searchsoftwarequality/definition/polyglot-programming&#34;&gt;Polyglot&lt;/a&gt; &amp;mdash; One of the biggest things in the ILE environment IMO is the 
possiblitly to bind multiple modules coded in different programming languages together to a 
single program object. Coupled with the fact that one can use any exported symbol (function, 
procedure, data) from any ILE capable language makes way for many possiblities and let you 
choose the right language for the task. F. e. C for networking stuff and RPG for business 
logic and data processing. &lt;a href=&#34;https://github.com/sitemule/ILEastic&#34;&gt;ILEastic&lt;/a&gt; is my favorite example for demonstrating this.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Thread_safety&#34;&gt;Thread safety&lt;/a&gt; &amp;mdash; Most of the time the normal RPG developer won’t have 
to bother about threads and thread safety. But if your application or framework makes use of 
multiple threads you need to make sure that your code is thread safe. This is super easy in 
RPG. Just add a control option to your code like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ctl-opt thread(*concurrent);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Note: If you are using embedded SQL and transactions this control option will not be enough 
  because transactions are scoped to the job or activation group. One solution to this is to 
  activate the &lt;a href=&#34;https://www.ibm.com/docs/en/i/7.4?topic=ccc-sql-server-mode-thread-scoped-transactions-commitment-control&#34;&gt;SQL Server Mode&lt;/a&gt; with the &lt;a href=&#34;https://www.ibm.com/docs/en/i/7.4?topic=ssw_ibm_i_74/apis/qwtchgjb.html&#34;&gt;QWTCHGJB&lt;/a&gt; API.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data queues &amp;mdash; This is more a platform feature than a programming language feature.
But I add it anyway as it is a great tool for developers. Queues are also natively available
on other platforms but rather as in memory data structures (not RPG data structures) to 
operate in one process and not to communicate between different processes as is often the 
case on IBM i. So if you have a need to decouple programs but don’t want to install a full 
blown message queueing system you can often easily achieve things with a data queue (when 
it comes to one-to-one communications, no pub&amp;#x2F;sub support). Data queues can also be peristent 
and really fast.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So these are 12 features of RPG and ILE I really like. There are definitely more great features.&lt;/p&gt;
&lt;p&gt;What are your top things you like about RPG and the ILE environment?&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2022/08/28/2022-08-28-toc-update/</guid>
            <title>Tour of Champions - Update</title>
            <link>http://example.com/2022/08/28/2022-08-28-toc-update/</link>
            <category>RPG</category>
            <pubDate>Sun, 28 Aug 2022 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;A little while ago … or even a little while longer ago :) … I made a tutorial about the
&lt;a href=&#34;https://github.com/sitemule/ILEastic&#34;&gt;ILEastic&lt;/a&gt; microservice framework : 
&lt;a href=&#34;https://champions.rpgnextgen.com/&#34;&gt;Tour of Champions&lt;/a&gt;. It covered making a web service from
a template project in a modular style and with SQL for the database access.&lt;/p&gt;
&lt;p&gt;And much of my work is to make the life of a developer easier. No matter if it is in the open 
source world or in a paid environment.&lt;/p&gt;
&lt;p&gt;So … can it get easier?&lt;/p&gt;
&lt;h2 id=&#34;Easier&#34;&gt;&lt;a href=&#34;#Easier&#34; class=&#34;headerlink&#34; title=&#34;Easier ?&#34;&gt;&lt;/a&gt;Easier ?&lt;/h2&gt;&lt;p&gt;At the time the original tutorial was written you had to compile ILEastic and its dependencies
all by yourself. Only the projects Linked List and Message were pre-packaged and could be installed
with the &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg&#34;&gt;iPKG&lt;/a&gt; client.&lt;/p&gt;
&lt;p&gt;This has changed. Now there are also packages for ILEastic and &lt;a href=&#34;https://github.com/sitemule/noxDB&#34;&gt;noxDB2&lt;/a&gt; 
available which makes it much easier for you to give this tutorial a try and see how easy it is 
to write web services in RPG without having to switch to any other language (though you could of 
course use any ILE enabled language in your web service program with ease).&lt;/p&gt;
&lt;p&gt;I updated the tutorial so you can have a smooth ride through each section.&lt;/p&gt;
&lt;p&gt;The biggest change to the previous setup is that the packaged ILEastic version has been compiled
with noxDB2 (which is the UTF8 branch in the Github repository) and is not compatible with the 
version on Github project site.&lt;/p&gt;
&lt;p&gt;Also thanks and kudos to Niels Liisberg for creating these wonderful projects 
&lt;a href=&#34;https://github.com/sitemule/noxDB&#34;&gt;noxDB&lt;/a&gt; and &lt;a href=&#34;https://github.com/sitemule/ILEastic&#34;&gt;ILEastic&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I am very grateful for any feedback.&lt;/p&gt;
&lt;p&gt;Happy web servicing!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2022/04/24/2022-04-24-ileastic-even-easier/</guid>
            <title>ILEastic ... can it get even easier?</title>
            <link>http://example.com/2022/04/24/2022-04-24-ileastic-even-easier/</link>
            <category>RPG</category>
            <category>iPKG</category>
            <pubDate>Sun, 24 Apr 2022 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;&lt;a href=&#34;https://github.com/sitemule/ILEastic&#34;&gt;ILEastic&lt;/a&gt; is a microservice framework written mostly in C
and available as an ILE service program. That means we can build web service in ILE languages 
like RPG and COBOL (and C of course). No more jumping through hoops to offer a web service to 
your colleagues or business partners.&lt;/p&gt;
&lt;h2 id=&#34;Easier&#34;&gt;&lt;a href=&#34;#Easier&#34; class=&#34;headerlink&#34; title=&#34;Easier ?&#34;&gt;&lt;/a&gt;Easier ?&lt;/h2&gt;&lt;p&gt;Of course we can do&amp;#x2F;make it easier :) . Because getting ILEastic onto your server has been a pain 
so far for anyone not familiar with the various projects (namely ILEastic, noxDB and ILEfastCGI) 
and the tools used for building the projects. Offering a simple save file has not been very easy so
far because your system may have a different CCSID as the one used for building the projects which
may lead to incompatibilites.&lt;/p&gt;
&lt;p&gt;But now I can offer you a nice RPM package which can be installed with 
&lt;a href=&#34;https://bitbucket.org/m1hael/ipkg&#34;&gt;iPKG&lt;/a&gt;. To install ILEastic with iPKG we just need the iPKG 
client on our machine (available &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/downloads/&#34;&gt;here&lt;/a&gt;) and execute
the install command.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install ileastic&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;But before we can execute this command we need to tell iPKG where it can get the RPM packages from.
This can be done with a simple command:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg addrepo &amp;#x27;RPGNextGen https://repo.rpgnextgen.com/repository&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;This will create the necessary environment for iPKG. By default it will use the library &lt;code&gt;IPKG&lt;/code&gt;
but you can change that by adding the parameter &lt;code&gt;IPKGLIB(YOUR_LIB)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After telling iPKG &lt;em&gt;where&lt;/em&gt; it can find packages we need to tell it &lt;em&gt;which&lt;/em&gt; packages can be
found at the repository we configured. For this we just need another simple command:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg update&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Coming back to our original intent of installing ILEastic with the command:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install ileastic&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Hmmm … it failed … and that is the correct result. Because ILEastic depends on 
&lt;a href=&#34;https://github.com/sitemule/noxDB&#34;&gt;noxDB&lt;/a&gt; we need to install noxDB before installing ILEastic 
(anybody remembering the old days of &lt;code&gt;rpm&lt;/code&gt; where you manually needed to install every required 
package before installing the package you originally wanted to install?!). Automatically 
installing all dependencies is definitely on my list but needs much work and won’t be available 
in the near future. So here we go with first installing noxDB:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install noxdb2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now we can install ILEastic (see the install command above).&lt;/p&gt;
&lt;p&gt;Note that we don’t need to install the &lt;code&gt;noxdb2-devel&lt;/code&gt; package. That is the beauty of these 
packages. You just install what you need. For ILEastic itself we don’t need the prototypes of
noxDB. If you want to work with noxDB then of course you can install the noxDB prototypes.&lt;/p&gt;
&lt;p&gt;But as we want to use the ILEastic procedures we will need the ILEastic prototypes which are
available in the &lt;code&gt;ileastic-devel&lt;/code&gt; package. So we are going to install that one too.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install &amp;#x27;ileastic-devel&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;iPKG is not a global package manager. It works more like &lt;a href=&#34;https://docs.npmjs.com/about-npm&#34;&gt;NPM&lt;/a&gt; 
where packages are managed on a per project base (per library in our case). To make a package 
installation only for a single user you just need to specify a separate library on the commands. 
To have ILEastic installed in my own library and home directory I would call the following commands:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg addrepo &amp;#x27;RPGNextGen https://repo.rpgnextgen.com/repository&amp;#x27; ipkglib(MIHAEL)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ipkg update ipkglib(MIHAEL)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ipkg install ileastic ipkglib(MIHAEL)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ipkg install &amp;#x27;ileastic-devel&amp;#x27; ipkglib(MIHAEL) loc(&amp;#x27;/home/mihael/include&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;We can see which packages have been installed by executing a simple command:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg list installed&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;It will display the installed packages and their version.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ileastic (2.0.0)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ileastic-devel (2.0.0)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;noxdb2 (2.0.0)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;p&gt;If you want to know more about iPKG take a look at the 
&lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/wiki/Client&#34;&gt;project wiki&lt;/a&gt; and 
&lt;a href=&#34;https://blog.rpgnextgen.com/2020/04/16/ipkg-package-management-for-ile-in-beta-phase-and-now-what&#34;&gt;this&lt;/a&gt; 
and &lt;a href=&#34;https://blog.rpgnextgen.com/2020/05/21/i-can-package-it-all-by-myself&#34;&gt;this&lt;/a&gt; blog post.&lt;/p&gt;
&lt;p&gt;And now … how about writing a little web service?!&lt;/p&gt;
&lt;h2 id=&#34;First-Web-Service&#34;&gt;&lt;a href=&#34;#First-Web-Service&#34; class=&#34;headerlink&#34; title=&#34;First Web Service&#34;&gt;&lt;/a&gt;First Web Service&lt;/h2&gt;&lt;p&gt;Starting situation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;noxDB and ILEastic is installed in library MIHAEL (by using iPKG)&lt;/li&gt;
&lt;li&gt;NOXDB2 and ILEASTIC service programs are registered in the binding directory MIHAEL&amp;#x2F;IPKG (automatically done by iPKG)&lt;/li&gt;
&lt;li&gt;ILEastic prototypes are installed and located at&lt;code&gt;/home/mihael/include/ileastic/ileastic.rpgle&lt;/code&gt; (by using iPKG)&lt;/li&gt;
&lt;li&gt;library MIHAEL is part of the library list&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;Code&#34;&gt;&lt;a href=&#34;#Code&#34; class=&#34;headerlink&#34; title=&#34;Code&#34;&gt;&lt;/a&gt;Code&lt;/h3&gt;&lt;p&gt;We are now set for creating our first RPG web service program with the new ILEastic installation.
It will be a FREE RPG program (though we can also use it in a non FREE RPG program but it has to
be an ILE program). So our program starts with&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;**FREE&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;For this little example we compile it in one go with the command &lt;code&gt;CRTBNDRPG&lt;/code&gt; so we can add some 
compile options in the code (for production I would go with the two step approach of first 
creating a module with &lt;code&gt;CRTRPGMOD&lt;/code&gt; and then creating the program with &lt;code&gt;CRTPGM&lt;/code&gt;).&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ctl-opt dftactgrp(*no) actgrp(*caller) bnddir(&amp;#x27;IPKG&amp;#x27;) thread(*concurrent);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Note: The option &lt;code&gt;thread(*concurrent)&lt;/code&gt; is very important because ILEastic is a multi-threaded
      framework and all our web service programs and service programs are running in the same
      job but in multiple threads. This compile option makes sure the threads are not tripping 
      on each others toes when it comes to resource and memory management.&lt;/p&gt;
&lt;p&gt;Next we need to include the ILEastic prototypes.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;/include &amp;#x27;/home/mihael/include/ileastic/ileastic.rpgle&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;I like to directly jump into a procedure at program start without forfeiting the default error
handler.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;main();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;*inlr = *on;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;And here is our main procedure where we setup the web service and configure our end points &amp;#x2F; routes.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc main;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-ds config likeds(il_config);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  // The server will listen on port 44000.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  config.port = 44000;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  config.host = &amp;#x27;*ANY&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  // Add the end point / route.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  il_addRoute(config : %paddr(sayHello) : IL_GET : &amp;#x27;/hello&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  // Starts the server.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  il_listen(config);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Our end point will just return a nice “Hello!” to the requestor.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc sayHello;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-pi *n;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      request  likeds(IL_REQUEST);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      response likeds(IL_RESPONSE);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  end-pi;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  il_responseWrite(response : &amp;#x27;Hello!&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h3 id=&#34;Compile&#34;&gt;&lt;a href=&#34;#Compile&#34; class=&#34;headerlink&#34; title=&#34;Compile&#34;&gt;&lt;/a&gt;Compile&lt;/h3&gt;&lt;p&gt;This program can be compiled like any other FREE RPG program:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;CRTBNDRPG SRCFILE(MIHAEL/ILEASTIC) SRCMBR(ILHELLO) PGM(MIHAEL/ILHELLO)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;As our web service is just a normal ILE program we can start it by simply submitting it with the
&lt;code&gt;SBMJOB&lt;/code&gt; command.&lt;/p&gt;
&lt;h3 id=&#34;Start&#34;&gt;&lt;a href=&#34;#Start&#34; class=&#34;headerlink&#34; title=&#34;Start&#34;&gt;&lt;/a&gt;Start&lt;/h3&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;SBMJOB CMD(CALL PGM(ILHELLO)) JOB(ILEASTIC) JOBQ(QUSRNOMAX) CURLIB(MIHAEL) ALWMLTTHD(*YES)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Note: Make sure that the library list is set correctly so that the job has access to all necessary objects.&lt;/p&gt;
&lt;p&gt;Now you can query the web service with the tool of your choice like Insomnia, Postman, cURL, … or even 
your web browser.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://www.ibm.com/docs/en/i/7.4?topic=structure-netstat&#34;&gt;NETSTAT&lt;/a&gt; command with the parameter 
&lt;code&gt;*CNN&lt;/code&gt; is sometimes helpful to find the corresponding job because it allows us to select the job by 
the port it uses.&lt;/p&gt;
&lt;h2 id=&#34;What’s-next&#34;&gt;&lt;a href=&#34;#What’s-next&#34; class=&#34;headerlink&#34; title=&#34;What’s next?&#34;&gt;&lt;/a&gt;What’s next?&lt;/h2&gt;&lt;p&gt;As ILEastic is now also available via iPKG I might get some time to update the 
&lt;a href=&#34;https://champions.rpgnextgen.com&#34;&gt;ILEastic tutorial : Tour of Champions&lt;/a&gt;.
We’ll see :) .&lt;/p&gt;
&lt;p&gt;Also thanks and kudos to Niels Liisberg for creating these wonderful projects 
&lt;a href=&#34;https://github.com/sitemule/noxDB&#34;&gt;noxDB&lt;/a&gt; and &lt;a href=&#34;https://github.com/sitemule/ILEastic&#34;&gt;ILEastic&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy installing!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2022/02/26/2022-02-27-noxdb2-rpm-package/</guid>
            <title>noxDB ... now even easier</title>
            <link>http://example.com/2022/02/26/2022-02-27-noxdb2-rpm-package/</link>
            <category>RPG</category>
            <category>ipkg</category>
            <pubDate>Sat, 26 Feb 2022 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;&lt;a href=&#34;https://github.com/sitemule/noxDB&#34;&gt;noxDB&lt;/a&gt; - Not Only XML - is a software library which makes
it easy to parse and generate JSON and XML. Generating JSON or XML directly from an SQL is even 
easier with noxDB. noxDB has become my favorite tool when working with JSON or XML when I  want 
to process it with a &lt;a href=&#34;https://en.wikipedia.org/wiki/Object_model&#34;&gt;DOM&lt;/a&gt; like approach … which 
is most of the time.&lt;/p&gt;
&lt;h2 id=&#34;Even-Easier&#34;&gt;&lt;a href=&#34;#Even-Easier&#34; class=&#34;headerlink&#34; title=&#34;Even Easier&#34;&gt;&lt;/a&gt;Even Easier&lt;/h2&gt;&lt;p&gt;But to top it all off we can make it even easier. Till now it was a bit of a pain to install noxDB 
(depending on your environment, f. e. installed tools). This has been alleviated as we now have a
nice RPM package which can be used with &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg&#34;&gt;iPKG&lt;/a&gt;. To install noxDB 
with iPKG we just need the iPKG client on our machine (available 
&lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/downloads/&#34;&gt;here&lt;/a&gt;) and execute the install command.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install noxdb2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;This will install the noxDB service program (NOXDB2) in the library IPKG and also adds it to the
binding directory IPKG in the installation library. The installation library can be specified on
the install command with the parameter &lt;code&gt;IPKGLIB&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you want to use the service program you will need the RPG prototypes. These are also packaged
and can be installed in the same manner.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install &amp;#x27;noxdb2-devel&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The prototypes are saved in a stream file. By default this stream file will be placed in the IFS
in the folder &lt;code&gt;/usr/local/include&lt;/code&gt;. But this can also be changed on the install command with the
parameter &lt;code&gt;LOC&lt;/code&gt; (location).&lt;/p&gt;
&lt;p&gt;iPKG is not a global package manager. It works more like &lt;a href=&#34;https://docs.npmjs.com/about-npm&#34;&gt;NPM&lt;/a&gt;
where packages are managed on a per project base (per library in our case). To make a package 
installation only for a single user you just need to specify a separate library on the install 
command. To have noxDB installed in my own library and home directory I would call the following 
commands:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install noxdb2 ipkglib(MIHAEL)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ipkg install &amp;#x27;noxdb2-devel&amp;#x27; ipkglib(MIHAEL) loc(&amp;#x27;/home/mihael/include&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If you want to know more about iPKG take a look at the 
&lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/wiki/Client&#34;&gt;project wiki&lt;/a&gt; and 
&lt;a href=&#34;https://blog.rpgnextgen.com/2020/04/16/ipkg-package-management-for-ile-in-beta-phase-and-now-what&#34;&gt;this&lt;/a&gt; 
and &lt;a href=&#34;https://blog.rpgnextgen.com/2020/05/21/i-can-package-it-all-by-myself&#34;&gt;this&lt;/a&gt; blog post.&lt;/p&gt;
&lt;p&gt;Note: The noxdb2 package provides the &lt;code&gt;utf8&lt;/code&gt; branch of the noxDB project which is not yet on par 
      with the main branch when it comes to functionality but covers all the basics already.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Now that we have installed noxDB how to use it in RPG?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;Usage&#34;&gt;&lt;a href=&#34;#Usage&#34; class=&#34;headerlink&#34; title=&#34;Usage&#34;&gt;&lt;/a&gt;Usage&lt;/h2&gt;&lt;p&gt;Starting situation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;noxDB installed in library MIHAEL (service program NOXDB2)&lt;/li&gt;
&lt;li&gt;NOXDB2 registered in the binding directory MIHAEL&amp;#x2F;IPKG&lt;/li&gt;
&lt;li&gt;library MIHAEL is part of the library list&lt;/li&gt;
&lt;li&gt;noxDB2 prototypes installed in folder &lt;code&gt;/home/mihael/include&lt;/code&gt; (package &lt;code&gt;noxdb2-devel&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;prototypes available at &lt;code&gt;/home/mihael/include/noxdb2/noxDB2.rpgle&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We are now set for creating our first RPG program with the new noxDB installation. It will be a
FREE RPG program (though we can also use it in a non FREE RPG program but it has to be an ILE
program). So our program starts with&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;**FREE&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;For this little example we compile it in one go with the command &lt;code&gt;CRTBNDRPG&lt;/code&gt; so we can add some 
compile options in the code (for production I would go with the two step approach of first 
creating a module with &lt;code&gt;CRTRPGMOD&lt;/code&gt; and then creating the program with &lt;code&gt;CRTPGM&lt;/code&gt;).&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ctl-opt dftactgrp(*no) actgrp(*caller) bnddir(&amp;#x27;IPKG&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Next we need to include the noxDB prototypes.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;/include &amp;#x27;/home/mihael/include/noxdb2/noxDB2.rpgle&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;I like to directly jump into a procedure at program start without forfeiting the default error
handler.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;main();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;*inlr = *on;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;And here is our main procedure where we parse a JSON string, modify the noxDB object graph and 
output it with the &lt;code&gt;DSPLY&lt;/code&gt; opcode.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc main;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s json pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s s char(50);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  json = nox_parseString(&amp;#x27;&amp;#123; &amp;quot;game&amp;quot; : &amp;quot;Elden Ring&amp;quot; , &amp;quot;releaseDate&amp;quot; : &amp;quot;Q1/2022&amp;quot; &amp;#125;&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  nox_setStr(json : &amp;#x27;releaseDate&amp;#x27; : &amp;#x27;2022-02-24&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  nox_setStr(json : &amp;#x27;price&amp;#x27; : &amp;#x27;59,99 €&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  s = nox_asJsonText(json);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dsply s;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  on-exit;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    nox_close(json);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;h2 id=&#34;What’s-next&#34;&gt;&lt;a href=&#34;#What’s-next&#34; class=&#34;headerlink&#34; title=&#34;What’s next?&#34;&gt;&lt;/a&gt;What’s next?&lt;/h2&gt;&lt;p&gt;As noxDB is now available via iPKG providing ILEastic as a RPM package for usage with iPKG is on
my list.&lt;/p&gt;
&lt;p&gt;Also thanks and kudos to Niels Liisberg for creating these wonderful projects 
&lt;a href=&#34;https://github.com/sitemule/noxDB&#34;&gt;noxDB&lt;/a&gt; and &lt;a href=&#34;https://github.com/sitemule/ILEastic&#34;&gt;ILEastic&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy installing!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2021/12/29/2021-12-29-batch-test-run/</guid>
            <title>JBatch Example on IBM i - Test Run</title>
            <link>http://example.com/2021/12/29/2021-12-29-batch-test-run/</link>
            <category>Java</category>
            <pubDate>Wed, 29 Dec 2021 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;This will be the last episode of our Jakarta EE Batch adventure. In this post we will see our 
example application in action and see how to restart a batch job and the mechanics behind it.&lt;/p&gt;
&lt;h2 id=&#34;Test-Data&#34;&gt;&lt;a href=&#34;#Test-Data&#34; class=&#34;headerlink&#34; title=&#34;Test Data&#34;&gt;&lt;/a&gt;Test Data&lt;/h2&gt;&lt;p&gt;To have our application actually do anything we need some test data. I found a nice web site which
generates up to 100k data records and you can define how the data should look like. And it is even
for free. Great service! Thanks &lt;a href=&#34;https://onlinedatagenerator.com/&#34;&gt;onlinedatagenerator.com&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;We can download this test data set from the Bitbucket &lt;a href=&#34;https://bitbucket.org/m1hael/jbatch-example&#34;&gt;project site&lt;/a&gt; 
download section and upload it to the IFS on our IBM i server. The data is stored as &lt;code&gt;SQL INSERT&lt;/code&gt; 
statements and also includes the &lt;code&gt;SQL CREATE TABLE&lt;/code&gt; statement. We just need a library where we 
will store the data and we are good to go.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;RUNSQLSTM SRCSTMF(&amp;#x27;/home/mihael/customers.sql&amp;#x27;) COMMIT(*NONE)  DFTRDBCOL(RPGNEXTGEN)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Note: The data set contains 100k data records. Depending on your environment you may get some
messages in your job log and the job log may even overflow. So you might want to look out for an 
inquiry message which you need to answer.&lt;/p&gt;
&lt;h2 id=&#34;Install-Application&#34;&gt;&lt;a href=&#34;#Install-Application&#34; class=&#34;headerlink&#34; title=&#34;Install Application&#34;&gt;&lt;/a&gt;Install Application&lt;/h2&gt;&lt;p&gt;We can download a precompiled version of the application including all dependencies from the
download section of the project site. We can either run it from our PC, IBM i server or any
other server. Depending on the host you run the application you will need to adjust the 
configuration of the database connection.&lt;/p&gt;
&lt;h2 id=&#34;Run-Application&#34;&gt;&lt;a href=&#34;#Run-Application&#34; class=&#34;headerlink&#34; title=&#34;Run Application&#34;&gt;&lt;/a&gt;Run Application&lt;/h2&gt;&lt;p&gt;Our application is a Java batch application which can be executed in the same way as any other Java
application (and that is one reason I Iike it so much because you don’t have to jump through any 
hoops to get where you want to go):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Put every jar in the classpath and call the main class.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And we will do exactly that!&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;java -cp batch.jar:libs/* rpgnextgen.batch.Main start customerTransfer --customer 100&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;We also appended some parameters to the call.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ˋstartˋ : Action parameter (resolved by the class ˋrpgnextgen.batch.BatchStarterˋ&lt;/li&gt;
&lt;li&gt;ˋcustomerTransferˋ : Batch job id from the XML file under ˋMETA-INF&amp;#x2F;batch-jobsˋ&lt;/li&gt;
&lt;li&gt;ˋ–customer 100ˋ : These two parameters identify the customer to process. These parameters will get injected into our ˋCustomerReaderˋ class. Optional.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;Application-Logic&#34;&gt;&lt;a href=&#34;#Application-Logic&#34; class=&#34;headerlink&#34; title=&#34;Application Logic&#34;&gt;&lt;/a&gt;Application Logic&lt;/h2&gt;&lt;p&gt;The application either processes one customer (parameter &lt;em&gt;customer&lt;/em&gt;) or processes all &lt;strong&gt;active&lt;/strong&gt; 
customers starting from a given change date (parameter &lt;em&gt;date&lt;/em&gt;). If neither &lt;em&gt;customer&lt;/em&gt; nor &lt;em&gt;date&lt;/em&gt;
is provided the application will process all &lt;strong&gt;active&lt;/strong&gt; customers from the current date.&lt;/p&gt;
&lt;p&gt;Note: There are no active customers in the given data set. But feel free to modify the data as
      you want or need.&lt;/p&gt;
&lt;p&gt;Note: The &lt;em&gt;date&lt;/em&gt; parameter expects the date in ISO format like 2022-01-31 .&lt;/p&gt;
&lt;h2 id=&#34;Database-Configuration&#34;&gt;&lt;a href=&#34;#Database-Configuration&#34; class=&#34;headerlink&#34; title=&#34;Database Configuration&#34;&gt;&lt;/a&gt;Database Configuration&lt;/h2&gt;&lt;p&gt;But before we can call our nice little batch program we need to configure our database connection.&lt;/p&gt;
&lt;p&gt;Luckily we used MicroProfile Config for our configuration. Now we have multiple ways to get our 
configuration into the application. We have a base config file in &lt;code&gt;META-INF&lt;/code&gt; named 
&lt;code&gt;microprofile-config.properties&lt;/code&gt;. If we don’t do anything else the application will take database
configuration from this properties file.&lt;/p&gt;
&lt;p&gt;We can override these value by passing Java system properties on the call as additional parameters.&lt;/p&gt;
&lt;p&gt;But we can also use environment variables. So under linux we would just execute the following in a
shell:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;export ibmi_host=localhost&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;export ibmi_user=mihael&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;export ibmi_password=uwillneverknow&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;export ibmi_libraries=RPGNEXTGEN&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Note: You will need to adjust those values to match your environment and setup.&lt;/p&gt;
&lt;h2 id=&#34;Restart-Application&#34;&gt;&lt;a href=&#34;#Restart-Application&#34; class=&#34;headerlink&#34; title=&#34;Restart Application&#34;&gt;&lt;/a&gt;Restart Application&lt;/h2&gt;&lt;p&gt;The Jakarta EE Batch specification also states that a failed batch run needs to be able to be
restarted. For this to work you need to provide some &lt;em&gt;checkpoint&lt;/em&gt; information. Checkpoint
information can be used in the &lt;em&gt;ItemReader&lt;/em&gt; and&amp;#x2F;or &lt;em&gt;ItemWriter&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;And the usage is pretty direct and easy: You just implement the corresponding &lt;code&gt;checkpointInfo&lt;/code&gt;
methods where you return the checkpoint information for the current state of the reader or writer.
It can be any object which can be serialized (implements &lt;em&gt;Serializable&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;On a restart of an application the &lt;code&gt;open&lt;/code&gt; method is passed the last checkpoint information which
we provided in the &lt;code&gt;checkpointInfo&lt;/code&gt; method.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;@Override&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;public Serializable checkpointInfo() throws Exception &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	logger.finer(&amp;quot;Saving reader checkpoint data: &amp;quot; + lastReturnedCustomer);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	return lastReturnedCustomer == null ? null : lastReturnedCustomer.id;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;In a real application this could be the last processed customer number (object of class Integer),
see class &lt;code&gt;CustomerReader&lt;/code&gt; line 64. We would get that Integer on a restart as a parameter on the
&lt;code&gt;open&lt;/code&gt; method and are checking it at line 43.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;if (checkpoint != null) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	lastCustomerId = (Integer) checkpoint;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	logger.info(&amp;quot;Restarting after customer &amp;quot; + lastCustomerId);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If we have a &lt;code&gt;lastCustomerId&lt;/code&gt; we are starting our processing after that customer by specifying in
our SQL the &lt;code&gt;lastCustomerId&lt;/code&gt; as our offset to start at.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;@SqlQuery(&amp;quot;SELECT id, company, street, city, country, discount, active FROM customer &amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		+ &amp;quot;WHERE active = 1 AND id &amp;gt; :offset AND changeDate &amp;gt;= :changeDate&amp;quot;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ResultIterator&amp;lt;Customer&amp;gt; listActiveCustomers(@Bind(&amp;quot;offset&amp;quot;) int offset, @Bind(&amp;quot;changeDate&amp;quot;) LocalDate changeDate);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;blockquote&gt;
&lt;p&gt;But how do we actually restart the application?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For restarting the application we need the last failed batch job id (not IBM i job). We can get 
this from the log output of the original run. It states:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;rpgnextgen.batch.BatchStarter: Starting batch job with id 401&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;So &lt;code&gt;401&lt;/code&gt; is the batch job id we need to pass to on our restart.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;java -cp batch.jar:libs/* rpgnextgen.batch.Main restart 401 --date 2022-01-01&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Note: It is important to pass the same batch parameters to the restart as on the original batch run.
      Those parameters are not persisted by the framework.&lt;/p&gt;
&lt;h2 id=&#34;Wrap-Up&#34;&gt;&lt;a href=&#34;#Wrap-Up&#34; class=&#34;headerlink&#34; title=&#34;Wrap Up&#34;&gt;&lt;/a&gt;Wrap Up&lt;/h2&gt;&lt;p&gt;I think the batch specification is quite well written and fulfills my needs for batch processing
quite well. We can do all the necessary work in a structured way which lets us reuse components
and split up workloads in multiple ways and even restart batch runs. Great work!&lt;/p&gt;
&lt;p&gt;Happy batching!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2021/12/05/2021-12-05-batch-simplified/</guid>
            <title>JBatch Example on IBM i - simplified</title>
            <link>http://example.com/2021/12/05/2021-12-05-batch-simplified/</link>
            <category>Java</category>
            <pubDate>Sun, 05 Dec 2021 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;The JBatch Example in my last post was ok for a prototype but when you have to write many batch
jobs we would rather want to have to code less and have more ready-to-use components.
This can be done very easily in only a few steps by putting these reusable components into a 
&lt;code&gt;batch.core&lt;/code&gt; project which we can later declare as a depencency in your batch projects.&lt;/p&gt;
&lt;h2 id=&#34;Dependencies&#34;&gt;&lt;a href=&#34;#Dependencies&#34; class=&#34;headerlink&#34; title=&#34;Dependencies&#34;&gt;&lt;/a&gt;Dependencies&lt;/h2&gt;&lt;p&gt;As this is about using &lt;a href=&#34;https://github.com/WASdev/standards.jsr352.jbatch&#34;&gt;JBatch&lt;/a&gt; as our batch 
framework of choice for every batch project we can put all those Maven dependencies into the 
&lt;code&gt;batch.core&lt;/code&gt; project. Now the only direct dependency we need to specify in our batch project is 
&lt;code&gt;batch.core&lt;/code&gt; which leaves us with a very clean &lt;code&gt;pom.xml&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Note: You still need to specify how the application will be build in the &lt;code&gt;build&lt;/code&gt; section. If this
      becomes more complex we could create our own custom packaging type &lt;code&gt;batch&lt;/code&gt; in a separate 
      maven plugin project and just specify that plugin in our build section. But that would be
      overkill at the moment.&lt;/p&gt;
&lt;h2 id=&#34;Database-connection&#34;&gt;&lt;a href=&#34;#Database-connection&#34; class=&#34;headerlink&#34; title=&#34;Database connection&#34;&gt;&lt;/a&gt;Database connection&lt;/h2&gt;&lt;p&gt;As we are primarily working with our beloved DB2 on IBM i why not have a database connection
readily available for usage?! We just have to create a Jdbi instance provider which takes our
database connection configuration via MicroProfile Config, create a DataSource instance and 
feed that to the Jdbi class which returns a fresh Jdbi instance to be used.&lt;/p&gt;
&lt;p&gt;Note: Keep in mind that the Jdbi instance itself is no database connection. We get a database
      connection by retrieving a Handle instance from the Jdbi instance.&lt;/p&gt;
&lt;p&gt;The code may look like this.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;@ApplicationScoped&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;public class IbmiDataSourceProvider &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	@Produces&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	@Named(&amp;quot;ibmiDataSource&amp;quot;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	public DataSource provideDataSource() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		Config config = ConfigProvider.getConfig();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		String host = config.getValue(&amp;quot;ibmi.host&amp;quot;, String.class);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		String user = config.getValue(&amp;quot;ibmi.user&amp;quot;, String.class);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		String password = config.getValue(&amp;quot;ibmi.password&amp;quot;, String.class);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		String libraries = config.getValue(&amp;quot;ibmi.libraries&amp;quot;, String.class);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		AS400JDBCDataSource ds = new AS400JDBCDataSource(host, user, password);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		ds.setLibraries(libraries);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		return ds;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;This code only supports a single database connection configuration (which is my use case) but
can be extended to support multiple database connections to different machines.&lt;/p&gt;
&lt;p&gt;Note: This will be even more compact when SmallRye Config switches to the &lt;code&gt;jakarta&lt;/code&gt; namespace
      and we can use CDI to access the configuration.&lt;/p&gt;
&lt;p&gt;Note: &lt;a href=&#34;https://helidon.io/&#34;&gt;Helidon&lt;/a&gt; has an extension which builds an injectable DataSource 
      instance purely from configuration. Very slick and developer friendly! :)&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;@ApplicationScoped&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;public class IbmiDatabaseProvider &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	@Inject&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	@Named(&amp;quot;ibmiDataSource&amp;quot;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	DataSource dataSource;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	private Jdbi jdbi;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	@PostConstruct&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	public void postConstruct() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		jdbi = Jdbi.create(dataSources.get());&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		jdbi.installPlugin(new SqlObjectPlugin());&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	@Produces&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	public Jdbi provideJdbi() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		return jdbi;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;Utility-Classes&#34;&gt;&lt;a href=&#34;#Utility-Classes&#34; class=&#34;headerlink&#34; title=&#34;Utility Classes&#34;&gt;&lt;/a&gt;Utility Classes&lt;/h2&gt;&lt;p&gt;The following utility classes will also make it into our new &lt;code&gt;batch.core&lt;/code&gt; project. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;BatchExecutorServiceProvider&lt;/li&gt;
&lt;li&gt;ConsoleWriter&lt;/li&gt;
&lt;li&gt;EndJobListener&lt;/li&gt;
&lt;li&gt;PassThroughItemProcessor&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;Application-Starter&#34;&gt;&lt;a href=&#34;#Application-Starter&#34; class=&#34;headerlink&#34; title=&#34;Application Starter&#34;&gt;&lt;/a&gt;Application Starter&lt;/h2&gt;&lt;p&gt;The classes &lt;code&gt;Main&lt;/code&gt; and &lt;code&gt;BatchStarter&lt;/code&gt; can also be moved to the &lt;code&gt;batch.core&lt;/code&gt; project as they have 
no project specific code. &lt;code&gt;Main&lt;/code&gt; is our entry point for the application and needs to be specified
on application start like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;java -cp batch.project.jar:libs/* batch.core.Main start batchJobId
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;The-Rest&#34;&gt;&lt;a href=&#34;#The-Rest&#34; class=&#34;headerlink&#34; title=&#34;The Rest&#34;&gt;&lt;/a&gt;The Rest&lt;/h2&gt;&lt;p&gt;This just leaves us with the batch project specific classes like the implementation of ItemReader, 
ItemProcessor, ItemWriter and database access classes like Jdbi DAO interfaces, mapper and data 
models.&lt;/p&gt;
&lt;p&gt;That are just 4 classes for the example project with just 152 lines of code. We just cut our
lines of code to a third of the original code base. I think that is not a bad result. &lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;jbatch-example/src/main/java/rpgnextgen/batch/data$ find . -name &amp;#x27;*.java&amp;#x27; | xargs wc -l&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  36 ./Customer.java&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  25 ./CustomerMapper.java&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  17 ./CustomerDao.java&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  74 ./CustomerReader.java&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; 152 insgesamt&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;And when it comes to configuration … as we are using &lt;a href=&#34;https://microprofile.io/project/eclipse/microprofile-config&#34;&gt;MicroProfile Config&lt;/a&gt; 
we don’t need to configure anything in the batch project itself as most configuration is done in 
the &lt;code&gt;batch.core&lt;/code&gt; project. We just need to override the &lt;code&gt;ibmi.user&lt;/code&gt; and &lt;code&gt;ibmi.password&lt;/code&gt; variable 
conveniently via environment variables on the target system. Very easy to do.&lt;/p&gt;
&lt;p&gt;Note: We probably can even do without &lt;em&gt;any&lt;/em&gt; configuration at all because the IBM Toolbox for Java 
      supports using the user of the current job. AFAIK you would need to specify &lt;em&gt;localhost&lt;/em&gt; for
      the server and &lt;em&gt;*CURRENT&lt;/em&gt; for the user. But for some reason that didn’t work. If anybody
      has come up with the solution for this I would be very happy for a message on how to do it.&lt;/p&gt;
&lt;p&gt;Happy simplifying!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2021/11/28/2021-11-27-batch-example-on-i/</guid>
            <title>JBatch Example on IBM i</title>
            <link>http://example.com/2021/11/28/2021-11-27-batch-example-on-i/</link>
            <category>Java</category>
            <pubDate>Sun, 28 Nov 2021 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;My last blog post was about batch processing on IBM i with &lt;a href=&#34;https://jakarta.ee/specifications/batch/2.0/jakarta-batch-spec-2.0.html&#34;&gt;Jakarta EE Batch&lt;/a&gt;
implementation &lt;a href=&#34;https://github.com/WASdev/standards.jsr352.jbatch&#34;&gt;JBatch&lt;/a&gt; from IBM. I didn’t get 
into too much detail in that post as it was only to provide a brief overview of Jakarta EE Batch.
In this blog post I want to present and explain one of my JBatch example applications available 
on &lt;a href=&#34;https://bitbucket.org/m1hael/jbatch-example&#34;&gt;Bitbucket&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;Setup&#34;&gt;&lt;a href=&#34;#Setup&#34; class=&#34;headerlink&#34; title=&#34;Setup&#34;&gt;&lt;/a&gt;Setup&lt;/h2&gt;&lt;p&gt;For using JBatch you don’t have to do very much. You can use JBatch either with an Jakarta EE 
application server or even stand alone. I would like to use it standalone so I need to include all
the necessary Jakarta EE API projects in my application, f. e. JAX-B API, Transactions API, CDI 
API. And more or less that would be it. JBatch needs to save some batch runtime data somewhere.
I will be using &lt;a href=&#34;https://db.apache.org/derby/&#34;&gt;Apache Derby&lt;/a&gt; for that.&lt;/p&gt;
&lt;p&gt;Note: With pure JBatch you can get away with about 4 direct dependencies and all in all about 10
      resolved (including transitive) dependencies. Now compare that to any Node.js application
      which has a bit more functionality than a “hello world” example. That is one reason I find
      the Java world so much more compelling than the Node.js world.&lt;/p&gt;
&lt;p&gt;I will also use the &lt;a href=&#34;https://microprofile.io/project/eclipse/microprofile-config&#34;&gt;MicroProfile Config&lt;/a&gt;
implementation &lt;a href=&#34;https://smallrye.io/docs/smallrye-config/index.html&#34;&gt;SmallRye Config&lt;/a&gt;. Sadly 
SmallRye Config still uses the &lt;code&gt;javax&lt;/code&gt; namespace instead of the new &lt;code&gt;jakarta&lt;/code&gt; namespace. But that 
will probably change in the future. So we will need to include the javax-API dependency to our 
project and cannot use the ConfigExtension to integrate it with CDI.&lt;/p&gt;
&lt;p&gt;To get all those moving parts work together I will be using &lt;a href=&#34;https://weld.cdi-spec.org/&#34;&gt;JBoss Weld&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For accessing the database I will use the &lt;a href=&#34;https://jdbi.org/&#34;&gt;Jdbi&lt;/a&gt; project. Jdbi eases the usage 
of SQL in Java without getting in your way. The result is a very small and clean code base for 
your database access layer. Kudos to the Jdbi project.&lt;/p&gt;
&lt;p&gt;And of course we need a database driver and as we access our beloved IBM i server we will use the 
&lt;a href=&#34;http://jt400.sourceforge.net/&#34;&gt;JTOpen&lt;/a&gt; project.&lt;/p&gt;
&lt;p&gt;All in all I end up with having 14 direct dependencies and with 45 resolved dependencies. Again …
compare that to a Node.js batch application. Actually I am currently working on another open source
Node.js application and &lt;code&gt;ls -1 node_modules | wc -l&lt;/code&gt; counts 169 libraries.&lt;/p&gt;
&lt;p&gt;Now let’s take a look at the actual code.&lt;/p&gt;
&lt;h2 id=&#34;Start-Application&#34;&gt;&lt;a href=&#34;#Start-Application&#34; class=&#34;headerlink&#34; title=&#34;Start Application&#34;&gt;&lt;/a&gt;Start Application&lt;/h2&gt;&lt;p&gt;The application starts in the &lt;code&gt;batch.Main&lt;/code&gt; class. First we will start the CDI container and 
initialize it:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;weld = new Weld().containerId(&amp;quot;rpgnextgen.batch&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;weld.initialize();&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;More or less that’s it for this class.&lt;/p&gt;
&lt;h2 id=&#34;Start-Batch-Job&#34;&gt;&lt;a href=&#34;#Start-Batch-Job&#34; class=&#34;headerlink&#34; title=&#34;Start Batch Job&#34;&gt;&lt;/a&gt;Start Batch Job&lt;/h2&gt;&lt;p&gt;Our application needs to wait till the CDI container is ready. It will fire a &lt;code&gt;ContainerInitialized&lt;/code&gt;
event when it is ready. The &lt;code&gt;batch.BatchStarter&lt;/code&gt; class listens for this event to actually start the
batch job.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;public void startImport(@Observes ContainerInitialized event, @Named(&amp;quot;commandLineArguments&amp;quot;) List&amp;lt;String&amp;gt; parameters) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      ...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If the event is received it parses the command line arguments to see what action to take and what
batch job to start or restart. &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;start customerTransfer --customer 100 --date 2021-01-31
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Also every additional parameter pair is added to the batch properties which are passed to the batch
job. Those batch properties can be injected into the batch classes like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;@Inject&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;@BatchProperty&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;private String customer;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;With the information we can now actually start the batch job like this.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;private void runBatch(String batchName, Properties batchProperties) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      BatchExecutorServiceProvider executorServiceProvider = new BatchExecutorServiceProvider();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      BatchSPIManager batchSPIManager = BatchSPIManager.getInstance();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      batchSPIManager.registerPlatformMode(BatchSPIManager.PlatformMode.SE);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      batchSPIManager.registerExecutorServiceProvider(executorServiceProvider);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      long jobExecId = BatchRuntime.getJobOperator().start(batchName, batchProperties);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      logger.info(&amp;quot;Starting batch job with id &amp;quot; + jobExecId);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;h2 id=&#34;Restart-Batch-Job&#34;&gt;&lt;a href=&#34;#Restart-Batch-Job&#34; class=&#34;headerlink&#34; title=&#34;Restart Batch Job&#34;&gt;&lt;/a&gt;Restart Batch Job&lt;/h2&gt;&lt;p&gt;To restart a batch job the job execution id is needed which is outputted to the log on starting the
batch job. The command line arguments look very similar to the previous ones.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;restart &amp;lt;job exec id&amp;gt; --customer 100 --date 2021-01-31
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The batch properties appended as parameters to the command line arguments &lt;strong&gt;need&lt;/strong&gt; to be passed
again.&lt;/p&gt;
&lt;p&gt;Instead of calling the &lt;code&gt;JobOperator::start&lt;/code&gt; method it calls the &lt;code&gt;JobOperator::restart&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;BatchRuntime.getJobOperator().restart(execId, batchProperties);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;Step-Execution&#34;&gt;&lt;a href=&#34;#Step-Execution&#34; class=&#34;headerlink&#34; title=&#34;Step Execution&#34;&gt;&lt;/a&gt;Step Execution&lt;/h2&gt;&lt;p&gt;Jakarta EE Batch supports the &lt;code&gt;Input &amp;amp;rarr; Processing &amp;amp;rarr; Output&lt;/code&gt; type of flow with the 
provided interface &lt;code&gt;ItemReader&lt;/code&gt;, &lt;code&gt;ItemProcessor&lt;/code&gt; and &lt;code&gt;ItemWriter&lt;/code&gt; but it also supports a much 
simpler and task-oriented style (not coupled to any input items) with &lt;code&gt;Batchlet&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Our declared steps for this job look like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;step id=&amp;quot;step1&amp;quot;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &amp;lt;chunk item-count=&amp;quot;100&amp;quot;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;reader ref=&amp;quot;rpgnextgen.batch.data.CustomerReader&amp;quot;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                  &amp;lt;properties&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        &amp;lt;property name=&amp;quot;customer&amp;quot; value=&amp;quot;#&amp;#123;jobParameters[&amp;#x27;customer&amp;#x27;]&amp;#125;&amp;quot; /&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        &amp;lt;property name=&amp;quot;date&amp;quot; value=&amp;quot;#&amp;#123;jobParameters[&amp;#x27;date&amp;#x27;]&amp;#125;&amp;quot; /&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                  &amp;lt;/properties&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;/reader&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;processor ref=&amp;quot;rpgnextgen.batch.util.PassThroughItemProcessor&amp;quot; /&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;writer ref=&amp;quot;rpgnextgen.batch.util.ConsoleWriter&amp;quot; /&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &amp;lt;/chunk&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/step&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Data is read from the database in the &lt;code&gt;CustomerReader&lt;/code&gt; class. The class will get the &lt;code&gt;customer&lt;/code&gt; 
and &lt;code&gt;date&lt;/code&gt; batch property injected (if passed).&lt;/p&gt;
&lt;p&gt;All read items from the reader are just passed through by the &lt;code&gt;PassThroughItemProcessor&lt;/code&gt; (that
would be the place where we would do any validation or transformation).&lt;/p&gt;
&lt;p&gt;And all processed items are outputted to the console by the &lt;code&gt;ConsoleWriter&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;Reading-Data&#34;&gt;&lt;a href=&#34;#Reading-Data&#34; class=&#34;headerlink&#34; title=&#34;Reading Data&#34;&gt;&lt;/a&gt;Reading Data&lt;/h2&gt;&lt;p&gt;The CustomerReader retrieves the data from the database. For convenience it extends 
&lt;code&gt;AbstractItemReader&lt;/code&gt; which is provided by JBatch. The &lt;code&gt;open&lt;/code&gt; method is where the 
initialization gets done like getting a database connection (Jdbi Handle instance) and in our
case we also execute the SQL query already in the &lt;code&gt;open&lt;/code&gt; method but don’t retrieve any items
from the result set yet.&lt;/p&gt;
&lt;p&gt;The actual &lt;code&gt;readItem&lt;/code&gt; method is rather short and we even can get away with a one-liner. Our DAO 
object returned an iterator from the SQL query. We can now just query the iterator for the next 
customer.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;return lastReturnedCustomer = activeCustomers.hasNext() ? activeCustomers.next() : null;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Note: The last returned customer is temporarily saved for later use when it comes to check points
      and restarting the batch job.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;close&lt;/code&gt; method is all about finishing and cleaning up like closing any database connections.&lt;/p&gt;
&lt;h2 id=&#34;Jdbi&#34;&gt;&lt;a href=&#34;#Jdbi&#34; class=&#34;headerlink&#34; title=&#34;Jdbi&#34;&gt;&lt;/a&gt;Jdbi&lt;/h2&gt;&lt;p&gt;For database access Jdbi is used in this project. Jdbi’s API comes in two flavors: 
Fluent API  and SQL objects.&lt;/p&gt;
&lt;p&gt;This project uses SQL objects from Jdbi. This is a pretty slick way of doing database queries. 
The SQL query is just defined as an annotation and Jdbi is doing the heavy lifting.&lt;/p&gt;
&lt;p&gt;It can look as simple as this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;@SqlQuery(&amp;quot;SELECT id, company, street, city, country, discount, active FROM customer WHERE id = :id&amp;quot;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ResultIterator&amp;lt;Customer&amp;gt; getCustomer(@Bind(&amp;quot;id&amp;quot;) int id);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;For mapping the raw SQL data to a &lt;code&gt;Customer&lt;/code&gt; object a Jdbi &lt;code&gt;RowMapper&lt;/code&gt; is used which just takes the
values from the result set and maps it to a &lt;code&gt;Customer&lt;/code&gt; object.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;@Override&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;public Customer map(ResultSet rs, StatementContext ctx) throws SQLException &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	Customer c = new Customer();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	c.active = rs.getInt(&amp;quot;active&amp;quot;) == 1;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	c.city = rs.getString(&amp;quot;city&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	c.country = rs.getString(&amp;quot;country&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	c.discount = rs.getBigDecimal(&amp;quot;discount&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	c.id = rs.getInt(&amp;quot;id&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	c.company = rs.getString(&amp;quot;company&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	c.street = rs.getString(&amp;quot;street&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	return c;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Could this have been coded with less lines of code by using some “magic”? Yes, definitely. But I
like simple code even if it means some lines more to write and this mapping can be understood by
any programmer from beginner to ninja.&lt;/p&gt;
&lt;h2 id=&#34;ItemProcessor&#34;&gt;&lt;a href=&#34;#ItemProcessor&#34; class=&#34;headerlink&#34; title=&#34;ItemProcessor&#34;&gt;&lt;/a&gt;ItemProcessor&lt;/h2&gt;&lt;p&gt;As the project uses the &lt;code&gt;Input &amp;amp;rarr; Processing &amp;amp;rarr; Output&lt;/code&gt; processing style of JBatch an 
&lt;code&gt;ItemProcessor&lt;/code&gt; needs to be implemented. In this example I don’t want to do anything with the item
but just passing it down the pipe to the output like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;public Object processItem(Object item) throws Exception &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	return item;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;h2 id=&#34;Writing-Data&#34;&gt;&lt;a href=&#34;#Writing-Data&#34; class=&#34;headerlink&#34; title=&#34;Writing Data&#34;&gt;&lt;/a&gt;Writing Data&lt;/h2&gt;&lt;p&gt;To make things simple I just write the customer data to the console using the &lt;code&gt;toString&lt;/code&gt; method of
the object. The &lt;code&gt;ConsoleWriter&lt;/code&gt; is easy to write and useful in many cases where you just want to 
test some workflows.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;public void writeItems(List&amp;lt;Object&amp;gt; items) throws Exception &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	items.forEach((c) -&amp;gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		System.out.println(c);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;		count++;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;As a little bonus &lt;code&gt;ConsoleWriter&lt;/code&gt; outputs the number of processed items at the end of processing
in the &lt;code&gt;ItemWriter::close&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;As we have only this one execution step that is our whole batch workflow.&lt;/p&gt;
&lt;h2 id=&#34;Dependencies&#34;&gt;&lt;a href=&#34;#Dependencies&#34; class=&#34;headerlink&#34; title=&#34;Dependencies&#34;&gt;&lt;/a&gt;Dependencies&lt;/h2&gt;&lt;p&gt;In this blog post I mentioned the number of dependencies compared to a Node.js application. RPG 
would probably win in a dependency contest. But that is also one problem in RPG: You don’t have 
so many open source projects to choose from and you end up with less functionality out-of-the-box
compared to other programming languages.&lt;/p&gt;
&lt;p&gt;When I take a look at the libraries I pulled into this project I notice that almost all of them 
come from very reliable sources like IBM, Red Hat, Eclipse and Apache. I have almost no library
where I cannot immediately associate it with a reputable and reliable source. Again … compare 
that to a Node.js application.&lt;/p&gt;
&lt;h2 id=&#34;Wrap-Up&#34;&gt;&lt;a href=&#34;#Wrap-Up&#34; class=&#34;headerlink&#34; title=&#34;Wrap Up&#34;&gt;&lt;/a&gt;Wrap Up&lt;/h2&gt;&lt;p&gt;It may look like very much code for very little functionality but with this setup we have everything
neatly seperated and in small chunks of code which is much easier to grasp than those RPG monoliths
which are thousands of lines long.&lt;/p&gt;
&lt;p&gt;And if we look at the lines of code per source file there are suprisingly few lines in each source
file. Not even one source file with more than 100 lines of code. Each source file fits on one screen
which makes it very easy to understand.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;$ find . -name &amp;#x27;*.java&amp;#x27; | xargs wc -l&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32 ./rpgnextgen/batch/jdbi/IbmiDataSourceProvider.java&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40 ./rpgnextgen/batch/jdbi/IbmiDatabaseProvider.java&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36 ./rpgnextgen/batch/data/Customer.java&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25 ./rpgnextgen/batch/data/CustomerMapper.java&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17 ./rpgnextgen/batch/data/CustomerDao.java&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74 ./rpgnextgen/batch/data/CustomerReader.java&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;89 ./rpgnextgen/batch/BatchStarter.java&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70 ./rpgnextgen/batch/Main.java&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28 ./rpgnextgen/batch/util/ConsoleWriter.java&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19 ./rpgnextgen/batch/util/EndJobListener.java&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12 ./rpgnextgen/batch/util/PassThroughItemProcessor.java&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23 ./rpgnextgen/batch/util/BatchExecutorServiceProvider.java&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;And even if this seems like much code and many classes we can pull many of these classes into a 
separate reusable Maven project and only need to write the code specific to our task. But this
will be covered in the next blog post. :)&lt;/p&gt;
&lt;p&gt;Happy batching!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2021/11/18/2021-11-18-batch-on-i/</guid>
            <title>JBatch on IBM i</title>
            <link>http://example.com/2021/11/18/2021-11-18-batch-on-i/</link>
            <category>Java</category>
            <pubDate>Thu, 18 Nov 2021 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;This whole blog has been very RPG centric so far. But some of you also know that I also do some 
Java programming (and also some Node.js+TypeScript from time to time). Lately I got involved into
some Java projects which also include IBM i as a potential target platform.&lt;/p&gt;
&lt;p&gt;Java on IBM i has a long history … not always with the best results. Java performance on IBM i
was not very good at the start but has improved drastically so that you probably don’t have to 
worry too much about any differences in runtime to other platforms. But that may depend on your 
requirements. If every microsecond counts you might be very picky about frameworks, the JRE and 
the platform it runs on.&lt;/p&gt;
&lt;p&gt;My opinion on that is that Java performance on IBM i for the SMB market is more than sufficient.
But as IBM i is not only used in the midrange market but also in the high end of computing 
side-by-side to main frames things may look different in these situations.&lt;/p&gt;
&lt;p&gt;But let us stay in the SMB market.&lt;/p&gt;
&lt;h2 id=&#34;Java-on-IBM-i&#34;&gt;&lt;a href=&#34;#Java-on-IBM-i&#34; class=&#34;headerlink&#34; title=&#34;Java on IBM i&#34;&gt;&lt;/a&gt;Java on IBM i&lt;/h2&gt;&lt;p&gt;The Java Runtime Environment OpenJ9 can be installed via &lt;code&gt;yum&lt;/code&gt; which will give you a JRE 11 on 
IBM i. Just recently the new long term support release of Java version 17 has been released. 
Hopefully Java 17 will soon come to the IBM i platform so that all the new nifty features like 
the &lt;a href=&#34;https://docs.oracle.com/en/java/javase/14/language/records.html&#34;&gt;new Record type&lt;/a&gt; can be used.&lt;/p&gt;
&lt;h2 id=&#34;Batch-Processing-on-IBM-i&#34;&gt;&lt;a href=&#34;#Batch-Processing-on-IBM-i&#34; class=&#34;headerlink&#34; title=&#34;Batch Processing on IBM i&#34;&gt;&lt;/a&gt;Batch Processing on IBM i&lt;/h2&gt;&lt;p&gt;The batch processing on IBM i was always the domain of RPG and CL. Java didn’t have any say in it
so far. But I think with the &lt;a href=&#34;https://jakarta.ee/specifications/batch/2.0/jakarta-batch-spec-2.0.html&#34;&gt;latest Jakarta EE Batch specification&lt;/a&gt;
to have the same feature set in RPG or CL you would have to do some serious development work.&lt;/p&gt;
&lt;p&gt;Some of those features are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;splitting up work into multiple threads (either splitting up the items or the work flow)&lt;/li&gt;
&lt;li&gt;reusing and mixing of predefined steps&lt;/li&gt;
&lt;li&gt;declaratively defining the workflow (in XML)&lt;/li&gt;
&lt;li&gt;restart jobs on failure at last failed transaction&lt;/li&gt;
&lt;li&gt;job history (runtime, completion status, …)&lt;/li&gt;
&lt;li&gt;listener interfaces (equivalent to exit points)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is not that those features are not doable in RPG or CL. In fact I think they are all doable in
RPG but they are not available by default and most RPG programs have not been written with those 
features in mind.&lt;/p&gt;
&lt;h2 id=&#34;Jakarta-EE-Batch-DIY&#34;&gt;&lt;a href=&#34;#Jakarta-EE-Batch-DIY&#34; class=&#34;headerlink&#34; title=&#34;Jakarta EE Batch + DIY&#34;&gt;&lt;/a&gt;Jakarta EE Batch + DIY&lt;/h2&gt;&lt;p&gt;I have searched for some time for a good framework for batch processing in Java but have not found
a package or product which supports everything I envisioned for my batch application. So I will 
have to put the pieces together by myself.&lt;/p&gt;
&lt;p&gt;Jakarta EE Batch looks like a good spec for batch processing in Java and JBatch implements it nicely.
The good thing is that I can use all the common Jakarta EE annotations and don’t have to learn a 
completely new framework or approach (like Spring) because I already got enough on my plate. So 
using the already widely used and supported approach and style is very convenient for me. Jakarta EE 
Batch also covers all the batch features I need.&lt;/p&gt;
&lt;p&gt;Note: Yes, Spring Batch has influenced the development of Jakarta EE Batch a lot but it is not the
      same. Jakarta EE Batch is also influence by some other frameworks and platforms.&lt;/p&gt;
&lt;p&gt;I could have just sticked to JBatch and be done. But there are so many ways and frameworks which 
make my life easier like CDI, MicroProfile Config, etc … which I also wanted to use. As I haven’t
found any prepackaged solution I just package it myself. So it is&lt;/p&gt;
&lt;p&gt;JBatch (Jakarta EE Batch) + Weld (CDI) + SmallRye Config (MP Config) + JDBI (database access)&lt;/p&gt;
&lt;h2 id=&#34;Results&#34;&gt;&lt;a href=&#34;#Results&#34; class=&#34;headerlink&#34; title=&#34;Results&#34;&gt;&lt;/a&gt;Results&lt;/h2&gt;&lt;p&gt;With this stack it is not only easier to write batch programs in Java but also has a pretty decent 
performance. One test case was to process a data set of 500k records from one table and update about
25k records (record by record) in another table. &lt;/p&gt;
&lt;p&gt;This was executed in a breeze by the RPG program in about 4 seconds. But the Java program did also 
not bad at all with 6 seconds (not taking into account the ramp-up time of the JVM and of the
frameworks). You can argue now that the Java program took 1.5 times longer to process the data but 
the table layout favored more the RPG style of execution as the data set to be processed could not 
be selected by any index because of the way the data was stored (f. e. dates stored as numbers in
the format ddmmyyyy :( ). &lt;/p&gt;
&lt;p&gt;So in different cases Java might actually be faster than RPG. But execution time is not always the 
most important factor. If some approach is fast enough other criteria might be much more 
interesting, f. e. low maintenance, available developers, active community, fast SDLC, future 
safety, … just to name a few.&lt;/p&gt;
&lt;h2 id=&#34;Cons&#34;&gt;&lt;a href=&#34;#Cons&#34; class=&#34;headerlink&#34; title=&#34;Cons&#34;&gt;&lt;/a&gt;Cons&lt;/h2&gt;&lt;p&gt;Some things might be implemented in an easier way. For example the creation of the data source is 
done manually. This might be done automatically with just some configuration entries, like in
Helidon.&lt;/p&gt;
&lt;p&gt;Also the dependency injection is not (fully) working in any class which is directly part of the 
JBatch process, like any ItemReader or ItemWriter as these are not created by Weld but by JBatch 
and thus don’t fully support CDI yet.&lt;/p&gt;
&lt;h2 id=&#34;Helidon&#34;&gt;&lt;a href=&#34;#Helidon&#34; class=&#34;headerlink&#34; title=&#34;Helidon&#34;&gt;&lt;/a&gt;Helidon&lt;/h2&gt;&lt;p&gt;&lt;a href=&#34;https://helidon.io/&#34;&gt;Helidon&lt;/a&gt; is a micro service framework from Oracle. It comes with everything 
you need in the micro service world. But it can also be used as a one-stop-shop for all those 
rudimentry things like CDI, data sources, etc. you need or are useful in a batch program. Helidon 
2.x does not support the new &lt;code&gt;jakarta&lt;/code&gt; namespace but sticks to the &lt;code&gt;javax&lt;/code&gt; namespace for the java 
enterprise packages. So it does not work with IBM JBatch 2.x because starting with the 2.0.0 
version IBM switched to the &lt;code&gt;jakarta&lt;/code&gt; namespace.&lt;/p&gt;
&lt;p&gt;But you can use Helidon with the JBatch 1.0.3 version. To get all the good stuff of Helidon you 
would probably need to use &lt;code&gt;helidon-microprofile-cdi&lt;/code&gt; as a parent Maven artifact in your project.
That artifact has most things included but the web server which you probably don’t need in your 
batch program. The result is a stripped down Helidon server which you can extend by adding Helidon
extensions.&lt;/p&gt;
&lt;h2 id=&#34;Apache-BatchEE&#34;&gt;&lt;a href=&#34;#Apache-BatchEE&#34; class=&#34;headerlink&#34; title=&#34;Apache BatchEE&#34;&gt;&lt;/a&gt;Apache BatchEE&lt;/h2&gt;&lt;p&gt;The Jakarta EE Batch implementation placed under the &lt;a href=&#34;https://geronimo.apache.org/batchee/&#34;&gt;Apache Geronimo&lt;/a&gt;
project also looks very promising and comes with some additional features like a management UI for
running batch processes. But the project does not seem to be very active and so I am sticking with 
&lt;a href=&#34;https://github.com/WASdev/standards.jsr352.jbatch&#34;&gt;JBatch&lt;/a&gt; from IBM.&lt;/p&gt;
&lt;h2 id=&#34;Example&#34;&gt;&lt;a href=&#34;#Example&#34; class=&#34;headerlink&#34; title=&#34;Example&#34;&gt;&lt;/a&gt;Example&lt;/h2&gt;&lt;p&gt;Actually I wanted to include an example application in this post but this one is already long enough
as it is so the JBatch example application be featured in another post.&lt;/p&gt;
&lt;p&gt;If somebody is successfully using any other Java based solution I would gladly &lt;a href=&#34;mailto:mihael@rpgnextgen.com&#34;&gt;like to here about it&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy batching!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2021/11/12/2021-11-11-punycode/</guid>
            <title>International Domain Names and RPG</title>
            <link>http://example.com/2021/11/12/2021-11-11-punycode/</link>
            <category>RPG</category>
            <pubDate>Fri, 12 Nov 2021 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;I just realized that I haven’t written any new blog entry for some months now. It is not that there
was nothing interesting to write about but most things were either not a topic for the blog or was 
too complex&amp;#x2F;big for the blog.&lt;/p&gt;
&lt;p&gt;This blog entry is about IDN or IDNA - International Domain Names (in Applications) - and Punycode.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Punycode … ? WTF is Punycode? &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Punycode is an encoding algorithm to encode Unicode strings to ASCII strings and vice versa in a 
lossless way. Non-ASCII characters will be removed from the string and appended to the string in 
an encoded form in only ASCII characters. The result is an ASCII string which can be decoded to
the original Unicode string.&lt;/p&gt;
&lt;h2 id=&#34;Use-Case-Domain-Name-Encoding&#34;&gt;&lt;a href=&#34;#Use-Case-Domain-Name-Encoding&#34; class=&#34;headerlink&#34; title=&#34;Use Case - Domain Name Encoding&#34;&gt;&lt;/a&gt;Use Case - Domain Name Encoding&lt;/h2&gt;&lt;p&gt;For almost two decades domain names can contain non-ASCII characters (for example german Umlaute).
This has been slowly implemented and supported by the software vendors and projects. In 2003
Punycode was introduced as a major building block for Internationalizing Domain Names in 
Applications (IDNA) to provide a way to convert domain names with unicode characters to an ASCII 
string and vice versa.&lt;/p&gt;
&lt;p&gt;One may think that today after almost two decades there is no further need for such things as 
Punycode … but that is far from the current state of affairs. Even today you may need to encode 
a domain name with Punycode into an ACE (ASCII Compatible Encoding) string which is a Punycode 
encoded string. If the original string contains non-ASCII characters it is prefixed with “xn–”. 
Also all characters are converted to lowercase.&lt;/p&gt;
&lt;p&gt;But not only in DNS server configuration &amp;#x2F; maintenance will you encounter ACE and Punycode. You
may also encounter it everywhere where you use a domain name, f. e. at such a common thing as an 
e-mail address.&lt;/p&gt;
&lt;h2 id=&#34;RPG-Support&#34;&gt;&lt;a href=&#34;#RPG-Support&#34; class=&#34;headerlink&#34; title=&#34;RPG Support&#34;&gt;&lt;/a&gt;RPG Support&lt;/h2&gt;&lt;p&gt;After a short while of searching the internet for a library which may be ported to either RPG or 
ILE C I found the projects libidn and its successor libidn2. These two libraries have also some 
bindings for other languages. But nothing for RPG so far.&lt;/p&gt;
&lt;p&gt;But after some more searching I have found &lt;a href=&#34;https://begriffs.com/posts/2019-05-23-unicode-icu.html&#34;&gt;a real good article about IDNA&lt;/a&gt; using a Punycode 
implementation from the &lt;a href=&#34;https://icu.unicode.org/&#34;&gt;ICU project&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;ICU … that should ring a bell. ICU (International Components for Unicode) is an open source 
project which is sponsored and supported by IBM. The implementations are primarily C&amp;#x2F;C++ and 
Java. But there are also ICU packages for many other programming languages. And after 
&lt;a href=&#34;https://www.ibm.com/docs/en/search/icu?scope=ssw_ibm_i_74&#34;&gt;searching ICU on IBM i Docs&lt;/a&gt; I was surprised to find many references to ICU and also an 
&lt;a href=&#34;https://www.ibm.com/docs/en/i/7.4?topic=category-international-components-unicode-apis&#34;&gt;RPG example&lt;/a&gt; using a function from the ICU library. Actually ICU is installed in the library 
QICU and provided as ILE service programs which means it is also usable in an RPG program. QICU also 
contains the binding directory QXICUAPIBD which has all latest ICU service programs registered.&lt;/p&gt;
&lt;h2 id=&#34;ICU-Punycode-Unicode-to-ASCII&#34;&gt;&lt;a href=&#34;#ICU-Punycode-Unicode-to-ASCII&#34; class=&#34;headerlink&#34; title=&#34;ICU Punycode - Unicode to ASCII&#34;&gt;&lt;/a&gt;ICU Punycode - Unicode to ASCII&lt;/h2&gt;&lt;p&gt;So far this looked really promising but my enthusiasm was quickly diminishing. Because besides the
mentioning of ICU I have found no further detailed information on how to use these available
service programs in my RPG program. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;@IBM: Where is the API documentation and RPG prototypes?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It seems there is none … or at least not readily available. So forget the easy way. Lets do it
the hard way. I took a closer look at the service programs and have found an exported function 
&lt;code&gt;u_strToPunycode_4_0&lt;/code&gt; which sounds exactly what I was searching for: &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Converting a unicode string to an ASCII string with Punycode.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ok. I had the name of the function but what about the parameters and return value? After browsing
through the source code of the ICU project I have found a header file: punycode.h&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; * u_strToPunycode() converts Unicode to Punycode.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; * The input string must not contain single, unpaired surrogates.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; * The output will be represented as an array of ASCII code points.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; * The output string is NUL-terminated according to normal ICU&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; * string output rules.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; * @param src Input Unicode string.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *            This function handles a limited amount of code points&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *            (the limit is &amp;gt;=64).&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *            U_INDEX_OUTOFBOUNDS_ERROR is set if the limit is exceeded.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; * @param srcLength Number of UChars in src, or -1 if NUL-terminated.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; * @param dest Output Punycode array.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; * @param destCapacity Size of dest.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; * @param caseFlags Vector of boolean values, one per input UChar,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *                  indicating that the corresponding character is to be&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *                  marked for the decoder optionally&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *                  uppercasing (TRUE) or lowercasing (FALSE)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *                  the character.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *                  ASCII characters are output directly in the case as marked.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *                  Flags corresponding to trail surrogates are ignored.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *                  If caseFlags==NULL then input characters are not&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *                  case-mapped.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; * @param pErrorCode ICU in/out error code parameter.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *                   U_INVALID_CHAR_FOUND if src contains&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *                   unmatched single surrogates.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *                   U_INDEX_OUTOFBOUNDS_ERROR if src contains&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *                   too many code points.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; * @return Number of ASCII characters in puny.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; * @see u_strFromPunycode&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;U_CFUNC int32_t&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;u_strToPunycode(const UChar *src, int32_t srcLength,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                UChar *dest, int32_t destCapacity,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                const UBool *caseFlags,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                UErrorCode *pErrorCode);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;That looks much more promising. Now to decode all the parts of the function header.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1  U_CFUNC int32_t&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2  u_strToPunycode(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3                  const UChar *src, &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4                  int32_t srcLength,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5                  UChar *dest, &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6                  int32_t destCapacity,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7                  const UBool *caseFlags,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8                  UErrorCode *pErrorCode);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;ol&gt;
&lt;li&gt;The return value is an &lt;code&gt;int32_t&lt;/code&gt; which is a 32-bit integer which translates to &lt;code&gt;int(10)&lt;/code&gt; in RPG. 
It represents the length of the encoded string (number of characters).&lt;/li&gt;
&lt;li&gt;The function name does not match the exported name 100%. The name is &lt;code&gt;u_strToPunycode&lt;/code&gt; and the 
exported name has the version appended: &lt;code&gt;u_strToPunycode_4_0&lt;/code&gt;. This can be easily solved by
setting the exported name in the &lt;code&gt;extproc&lt;/code&gt; keyword on the prototype.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now comes the more tricky part.&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;In the documention we can see that the strings are expected to be in UTF-16 character encoding, 
see &lt;a href=&#34;https://unicode-org.github.io/icu/userguide/dev/codingguidelines.html#source-code-strings-with-unicode-characters&#34;&gt;ICU Guidelines&lt;/a&gt;. So the passed string variable needs to have the data type &lt;code&gt;ucs2&lt;/code&gt; and 
CCSID &lt;code&gt;UTF-16&lt;/code&gt;. We also pass the variable by reference (as a pointer).&lt;/li&gt;
&lt;li&gt;The function either expects to get the size of the source string passed as the second parameter 
or get -1 passed to indicate that the source string is terminated with a hex null value (x’00’).
The length is passed to the function by value.&lt;/li&gt;
&lt;li&gt;The destination string which will contain the encoded string needs to be passed by reference 
(as a pointer) so that the function can write the encoded content to the variable.&lt;/li&gt;
&lt;li&gt;The size of the destination variable is passed as an integer by value.&lt;/li&gt;
&lt;li&gt;The function accepts hints about the case of the seperate characters. This is passed as an array
of UBool values. UBool translates to an int(10) in RPG. But lucky for us we can just pass &lt;code&gt;*NULL&lt;/code&gt;
indicating that the case should not be mapped.&lt;/li&gt;
&lt;li&gt;The last parameter is an error indicator. The ICU API declares and uses the &lt;code&gt;UErrorCode&lt;/code&gt; data type 
for the result of functions. &lt;code&gt;UErrorCode&lt;/code&gt; translates to &lt;code&gt;int(10)&lt;/code&gt; in RPG and holds 0 if successful.
Anything else is an error. The variable needs to be initialized with 0 before calling any ICU 
functions, see error handling in the ICU Guidelines.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With this information we can create the following prototype:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-pr u_strToPunycode int(10) extproc(*CWIDEN : &amp;#x27;u_strToPunycode_4_0&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  source pointer value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  sourceLength int(10) value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dest pointer value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  destLength int(10) value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  caseFlags pointer const;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  errorCode int(10);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-pr;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If you put all the puzzle pieces together the result may look like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;**FREE&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ctl-opt dftactgrp(*no) actgrp(*caller) bnddir(&amp;#x27;QICU/QXICUAPIBD&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// U_CFUNC int32_t&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// u_strToPunycode(const UChar *src, int32_t srcLength,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//                 UChar *dest, int32_t destCapacity,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//                 const UBool *caseFlags,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//                 UErrorCode *pErrorCode);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-pr u_strToPunycode int(10) extproc(*CWIDEN : &amp;#x27;u_strToPunycode_4_0&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  source pointer value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  sourceLength int(10) value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dest pointer value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  destLength int(10) value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  caseFlags pointer const;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  errorCode int(10);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-pr;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;main();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;*inlr = *on;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc main;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s domain ucs2(50) ccsid(*utf16);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s encoded ucs2(50) ccsid(*utf16);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s rc int(10);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s errorCode int(10);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  domain = &amp;#x27;Küchen.de&amp;#x27; + x&amp;#x27;00&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  rc = u_strToPunycode(%addr(domain) : -1 : %addr(encoded) : %size(encoded) : *null : errorCode);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  if (errorCode = 0);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    dsply &amp;#x27;Success&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  else;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    dsply &amp;#x27;Nope!&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  endif;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The variable &lt;code&gt;encoded&lt;/code&gt; now has the Punycode encoded string: kchen.de-65a&lt;/p&gt;
&lt;p&gt;Note: This is just the Punycode encoded value, not an ACE string. xn–kchen.de-65a would be the 
      corresponding ACE string.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;But hey! I am just an application developer. I have nothing to do with IDNs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yes, that might be. But f. e. sometimes you have to work with e-mail addresses and those contain 
domain names. Your e-mail or mailing list provider may expect ACE strings as domain names and reject
non-ASCII characters in domain names.&lt;/p&gt;
&lt;p&gt;This concludes our adventure with Punycode, IDNA and ICU.&lt;/p&gt;
&lt;p&gt;Happy punycoding!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2021/08/27/2021-08-27-noxdb-iledocs/</guid>
            <title>noxDB API Documentation</title>
            <link>http://example.com/2021/08/27/2021-08-27-noxdb-iledocs/</link>
            <category>RPG</category>
            <pubDate>Fri, 27 Aug 2021 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;I am using &lt;a href=&#34;https://github.com/sitemule/noxDB&#34;&gt;noxDB&lt;/a&gt; for some time now and I always stumbled 
upon some API calls where I just couldn’t remember either the exact function name, parameters 
or return value.&lt;/p&gt;
&lt;p&gt;Now it is not only all readily available in my &lt;a href=&#34;https://bitbucket.org/m1hael/iledocs/src&#34;&gt;ILEDocs&lt;/a&gt;
&lt;a href=&#34;http://iledocs.rpgnextgen.com/&#34;&gt;instance&lt;/a&gt; at &lt;a href=&#34;https://www.rpgnextgen.com/&#34;&gt;rpgnextgen.com&lt;/a&gt; for 
looking up the information but also for every editor which supports ILEDocs comments like 
&lt;a href=&#34;https://www.miworkplace.com/&#34;&gt;MiWorkplace&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/images/miworkplace-noxdb-iledocs2.png&#34; alt=&#34;MiWorkplace content-assist noxDB API&#34;&gt;&lt;/p&gt;
&lt;p&gt;This makes the life of the modern RPG developer so much easier.&lt;/p&gt;
&lt;p&gt;For everyone else you can still look up the API in the RPG copybook as the ILEDocs comments 
are side-by-side with the prototypes, constants and templates or take a look at the 
&lt;a href=&#34;https://sitemule.github.io/&#34;&gt;Sitemule tutorials&lt;/a&gt; at github.io.&lt;/p&gt;
&lt;p&gt;Happy mouse-hovering and CTRL+SPACEing!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2021/08/17/2021-08-17-kong-dashboard/</guid>
            <title>Kong Dashboard</title>
            <link>http://example.com/2021/08/17/2021-08-17-kong-dashboard/</link>
            <category>Kong</category>
            <pubDate>Tue, 17 Aug 2021 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;h1 id=&#34;Kong-Dashboard&#34;&gt;&lt;a href=&#34;#Kong-Dashboard&#34; class=&#34;headerlink&#34; title=&#34;Kong Dashboard&#34;&gt;&lt;/a&gt;Kong Dashboard&lt;/h1&gt;&lt;p&gt;Kong (or Kong Gateway) is a very lightweight API gateway. What is an API gateway?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An API gateway takes all API calls from clients, then routes them to the appropriate 
microservice with request routing, composition, and protocol translation. Typically 
it handles a request by invoking multiple microservices and aggregating the results, 
to determine the best path. It can translate between web protocols and web‑unfriendly 
protocols that are used internally. — NGINX&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It usually acts as a single entry point for API requests into your system. This
makes it easy to configure and implement cross-cutting concerns like authentication
and logging at a single point.&lt;/p&gt;
&lt;h2 id=&#34;Why-a-UI-project&#34;&gt;&lt;a href=&#34;#Why-a-UI-project&#34; class=&#34;headerlink&#34; title=&#34;Why a UI project?&#34;&gt;&lt;/a&gt;Why a UI project?&lt;/h2&gt;&lt;p&gt;Kong Gateway is not distributed with a UI. So configuring Kong Gateway is done
on the command line (which is not a bad thing per se). It also does not provide
a simple CLI but needs to be configured by executing HTTP requests. This makes
it not very user-friendly to configure and maintain.&lt;/p&gt;
&lt;p&gt;But on the other hand having web services to use for configuration makes it so
much easier to write a frontend by myself.&lt;/p&gt;
&lt;p&gt;So here we go … &lt;a href=&#34;https://github.com/m1h43l/kong-dashboard&#34;&gt;Kong Dashboard&lt;/a&gt;!&lt;/p&gt;
&lt;h2 id=&#34;Installation&#34;&gt;&lt;a href=&#34;#Installation&#34; class=&#34;headerlink&#34; title=&#34;Installation&#34;&gt;&lt;/a&gt;Installation&lt;/h2&gt;&lt;p&gt;I will not cover installing Kong. This has been done dozens of times. The website
at &lt;a href=&#34;https://konghq.com/install/&#34;&gt;konghq.com&lt;/a&gt; is a good place to start. Note: Do not use 
the DB-less mode as this mode cannot be used with the Admin API.&lt;/p&gt;
&lt;p&gt;Kong Gateway listens on two ports.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Port for every normal API request&lt;/li&gt;
&lt;li&gt;Port for Kong Gateway Admin API&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You configure both in the Kong Gateway configuration file. By default the Admin
API is not exposed to the outside world. It is only reachable on &lt;em&gt;localhost&lt;/em&gt;.
This makes it somewhat secure as anybody who wants to access the Admin API needs
to login to the server hosting the Kong Gateway.&lt;/p&gt;
&lt;h3 id=&#34;Self-hosting-the-Admin-API&#34;&gt;&lt;a href=&#34;#Self-hosting-the-Admin-API&#34; class=&#34;headerlink&#34; title=&#34;Self hosting the Admin API&#34;&gt;&lt;/a&gt;Self hosting the Admin API&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;But with this setup we cannot reach the Admin API with our web application.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One solution is to let Kong Gateway host the Admin API itself by adding a route&lt;/p&gt;
&lt;p style=&#34;text-align: center;&#34;&gt;
  &lt;img src=&#34;https://blog.rpgnextgen.com/images/kong-admin-route.png&#34; alt=&#34;Kong Admin API Route&#34;&gt;
&lt;/p&gt;
 
&lt;p&gt;and a service for the Admin API.&lt;/p&gt;
&lt;p style=&#34;text-align: center;&#34;&gt;
  &lt;img src=&#34;https://blog.rpgnextgen.com/images/kong-admin-service.png&#34; alt=&#34;Kong Admin API Service&#34;&gt;
&lt;/p&gt;


&lt;h3 id=&#34;Securing-the-Admin-API&#34;&gt;&lt;a href=&#34;#Securing-the-Admin-API&#34; class=&#34;headerlink&#34; title=&#34;Securing the Admin API&#34;&gt;&lt;/a&gt;Securing the Admin API&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Ok. But wait … now the Admin API is open for everybody to use.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yes. That is correct. But as it is now hosted by Kong Gateway we can use all
the features of Kong Gateway to make it more secure, f. e. configuring the
JWT plugin for the Admin API route.&lt;/p&gt;
&lt;p&gt;First we need to configure a consumer. Take a look at the 
&lt;a href=&#34;https://docs.konghq.com/gateway-oss/2.4.x/admin-api/#consumer-object&#34;&gt;Kong Gateway documentation&lt;/a&gt;
on how to add a consumer.&lt;/p&gt;
&lt;p&gt;Then we need to configure the JWT plugin at the just created consumer. The
documentation for the &lt;a href=&#34;https://docs.konghq.com/hub/kong-inc/jwt/&#34;&gt;JWT plugin&lt;/a&gt; 
is quite good and worth reading. If you are using JWT with the algorithm HS256 
you just need to pass a &lt;em&gt;key&lt;/em&gt; and a &lt;em&gt;secret&lt;/em&gt; like&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;key&amp;quot; : &amp;quot;my_key_which_identifies_token_generator&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;secret&amp;quot; : &amp;quot;my_secret_which_is_used_for_token_verification&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;The &lt;em&gt;key&lt;/em&gt; is the link between the configured JWT plugin in Kong Gateway and the
incoming JWT token. Kong Gateway searches for the configured &lt;em&gt;key&lt;/em&gt; in the incoming
JWT token. By default it searches for the &lt;em&gt;iss&lt;/em&gt; attribute in the JWT header and
payload part. The attribute can be configured in the JWT plugin configuration,
see &lt;em&gt;key_claim_name&lt;/em&gt;. It uses the secret from the first matching JWT plugin
configuration for token verification.&lt;/p&gt;
&lt;p&gt;We also want to check the expiration date of the token. This is not done by
default and must be configured. We can PATCH the configuration by sending&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123; &amp;quot;config&amp;quot; : &amp;#123; &amp;quot;claims_to_verify&amp;quot; : [&amp;quot;exp&amp;quot;] &amp;#125; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Last but not least we need to activate the plugin. We have configured the 
plugin but it will not be used yet. We can activate it in our defined route
&lt;em&gt;kong-admin&lt;/em&gt; by POSTing the following to &lt;em&gt;&amp;#x2F;routes&amp;#x2F;kong-admin&amp;#x2F;plugins&lt;/em&gt;&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123; &amp;quot;name&amp;quot; : &amp;quot;jwt&amp;quot; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Note: You also need to enable the CORS plugin either globally or for this route.&lt;/p&gt;
&lt;h3 id=&#34;Web-Application&#34;&gt;&lt;a href=&#34;#Web-Application&#34; class=&#34;headerlink&#34; title=&#34;Web Application&#34;&gt;&lt;/a&gt;Web Application&lt;/h3&gt;&lt;p&gt;The Kong Dashboard is an Angular web application and does not need an extra
web service layer. It communicates directly with the Kong Admin API. We can
take the transpiled Angular web application and drop it on any web server.&lt;/p&gt;
&lt;p style=&#34;text-align: center;&#34;&gt;
  &lt;img src=&#34;https://blog.rpgnextgen.com/images/kong-dashboard.png&#34; alt=&#34;Kong Dashboard Screenshot&#34;&gt;
&lt;/p&gt;

&lt;p&gt;If you are using a secured Admin API you can configure Kong Dashboard to
ask for a JWT token which will be added to the HTTP requests going to the
Admin API, see &lt;em&gt;assets&amp;#x2F;config.json&lt;/em&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;kong&amp;quot; : [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;quot;name&amp;quot; : &amp;quot;Kong Node 1&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;quot;url&amp;quot; : http://kong-node1/kong/api&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ],&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;auth&amp;quot; : &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;enabled&amp;quot; : true,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;protocol&amp;quot; : &amp;quot;oauth2&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;grant_type&amp;quot; : &amp;quot;client_credentials&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;url&amp;quot; : http://oauth2_provider/api/oauth&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;help&amp;quot;: https://github.com/m1h43l&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Note: Only OAuth2 grant type &lt;em&gt;client_credentials&lt;/em&gt; is supported at the moment.&lt;/p&gt;
&lt;h2 id=&#34;Wrap-Up&#34;&gt;&lt;a href=&#34;#Wrap-Up&#34; class=&#34;headerlink&#34; title=&#34;Wrap Up&#34;&gt;&lt;/a&gt;Wrap Up&lt;/h2&gt;&lt;p&gt;So this is my new open source project &lt;a href=&#34;https://github.com/m1h43l/kong-dashboard&#34;&gt;Kong Dashboard&lt;/a&gt;. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Is it feature-complete?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Definitely not! But it covers the basics like routes, services, consumers and 
a dashboard. My next task on the project will be to implement plugin configuration 
support.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Is is production stable?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We have used it for some months now and it worked fine for now. So I would give
it at least a beta version status.&lt;/p&gt;
&lt;p&gt;It would be great to get some feedback. What works for you and what doesn’t?
What are you missing the most in this new Kong UI project?&lt;/p&gt;
&lt;p&gt;Happy routing!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2021/06/13/2021-06-13-ileastic-sql-transactions%20copy/</guid>
            <title>ILEastic Tutorial - SQL Transactions</title>
            <link>http://example.com/2021/06/13/2021-06-13-ileastic-sql-transactions%20copy/</link>
            <category>RPG</category>
            <pubDate>Sun, 13 Jun 2021 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;In most IBM i shops SQL is the primary way of accessing data or is at least on its way to be it.
And this is a good thing. But as ILEastic is a multi threaded framework we need to be aware of 
the fact that multiple threads may be using SQL transactions simultaneously. We need to make 
sure that these threads don’t step on each others toes when it comes to transactions.&lt;/p&gt;
&lt;p&gt;IBM has a wonderful solution for this where you actually have to do almost nothing.&lt;/br&gt;
The answer is ( &lt;em&gt;&amp;ast;drumroll&amp;ast;&lt;/em&gt; ):  &lt;strong&gt;SQL Server Mode&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;From the IBM i Documentation web site:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There are cases where it is desirable for transactional work to be scoped to the thread, rather 
than an activation group. In other words, each thread has its own commitment definition and 
transactional work for each commitment definition is independent of work performed in other threads.
&lt;/br&gt;&lt;/br&gt;
Db2® for i provides this support by using the Change Job (QWTCHGJB) API to change the job to run 
in SQL server mode. When an SQL connection is requested in SQL server mode, it is routed to a 
separate job. All subsequent SQL operations that are performed for that connection are also routed 
to that job.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So we just have to call the Change Job API at the beginning of our web service program and we are
ready for thread scoped transactions. Great!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sometimes it is a bit tricky to call some system APIs. How does it look like with the Change Job API?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Not problem at all. We can easily encapsulate the code in a procedure and call that procedure at the
start of the web service program.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc setSqlServerMode;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-pr sys_changeJob extpgm(&amp;#x27;QWTCHGJB&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    job char(26) const;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    internalJobId char(16) const;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    format char(8) const;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    jobChangeInfo char(1000) const options(*varsize);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    errorCode likeds(qusec);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  end-pr;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-ds jobc0200 qualified template;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    numberKeys int(10) inz(1);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    fieldInfoLength int(10) inz(20);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    keyField int(10) inz(1922);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    dataType char(1) inz(&amp;#x27;C&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    reserved2 char(3);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    lengthData int(10) inz(1);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    data char(1) inz(&amp;#x27;1&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    reserved3 char(3);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  end-ds;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-ds error likeds(qusec) inz;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-ds jobData likeds(jobc0200) inz(*likeds);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  sys_changeJob(&amp;#x27;*&amp;#x27; : &amp;#x27;&amp;#x27; : &amp;#x27;JOBC0200&amp;#x27; : jobData : error);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Information about the Change Job API can be found at 
&lt;a href=&#34;https://www.ibm.com/docs/en/i/7.4?topic=ccc-sql-server-mode-thread-scoped-transactions-commitment-control&#34;&gt;IBM i Documentation&lt;/a&gt;. 
There is also a Technote in the Redbooks section from Scott Forstie called 
&lt;a href=&#34;https://www.redbooks.ibm.com/Redbooks.nsf/5193609f3941e9cf85256bc300724cfc/144c015527359d4c85257297005b2092&#34;&gt;DB2 for i5&amp;#x2F;OS: SQL Mode Primer&lt;/a&gt; 
(published in 2007). As we can see from the naming of the operating system this book is not brand 
new but there are many things we can learn from it.&lt;/p&gt;
&lt;p&gt;One important fact that I didn’t find on the new IBM i Documentation web site is which job 
attributes are passed from the main job to the SQL Server Mode job. Luckily this can be found 
in the previously mentioned Technote.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The following attributes are taken from the user profile:
…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A very important thing I missed on my first read of that Technote is that most attributes are
taken from the &lt;strong&gt;user profile&lt;/strong&gt; of the the main job and not the job attributes of the running job 
itself. So if you change the attributes (like the current library) in your program or on 
submitting your job it really doesn’t matter in regard to the SQL Server Mode job because the 
SQL Server Mode job will use the current library from the &lt;em&gt;user profile&lt;/em&gt; and not from the &lt;em&gt;running job&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The SQL Server Mode job also uses the default library list (system and user part). So we need to make
sure that every table we want to access via SQL in a non-qualified way (without specifying the 
library) is in the default library list configured in the system or in the current library of the 
user profile.&lt;/p&gt;
&lt;p&gt;Note: One last thing to mention is that I have an unresolved problem with the SQL MERGE statement 
when run in SQL Server Mode. In my case changing the MERGE statement to an INSERT and UPDATE
statement worked fine.&lt;/p&gt;
&lt;p&gt;You can find an example on how to use this in the &lt;a href=&#34;https://bitbucket.org/m1hael/tour-of-champions/src/webservices/src/web/champions.rpgmod&#34;&gt;Tour of Champions&lt;/a&gt;
demo project and you can also find this information in the &lt;a href=&#34;https://champions.rpgnextgen.com/Web_Services/SQL_Transactions.html&#34;&gt;Web Service&lt;/a&gt; section of the tutorial.&lt;/p&gt;
&lt;p&gt;If you have an idea what topic I should also cover in this series just drop me a mail.&lt;/p&gt;
&lt;p&gt;Happy microserving!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2021/06/06/2021-06-06-stomp-reloaded/</guid>
            <title>STOMP Reloaded</title>
            <link>http://example.com/2021/06/06/2021-06-06-stomp-reloaded/</link>
            <category>RPG</category>
            <pubDate>Sun, 06 Jun 2021 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;Most people have probably assumed that the next blog post will again be about 
“ILEastic Tutorial : Tour of Champions”. I will definitely continue this series but I had some time
to work on the STOMP client again. And this time I have working STOMP service program which implements
the STOMP specification 1.2.&lt;/p&gt;
&lt;p&gt;Background info: 
Some years ago I started my journey with developing an ILE STOMP client for communicating with the 
world outside of IBM i, specifically with message queueing systems like Artemis ActiveMQ and RabbitMQ. 
Back then the &lt;a href=&#34;http://stomp.github.io/stomp-specification-1.2.html&#34;&gt;current STOMP specification&lt;/a&gt; 
was version 1.0. Today we have the specification version 1.2 and a couple of features have been 
added like content types and heart-beating.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;But why even use an extra message queueing system? We already have native data queues on IBM i!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yes, we have data queues. And it is a great tool which is very easy to use. Thanks IBM (really :-) )!
But you need to use the right tool for the right job. With data queues we need to specify upfront 
the maximum message size. This can have a drastic impact on the maximum number of entries for a 
data queue. And you always have to work around the fact that the queue can be full. What to do with
any extra messages?&lt;/p&gt;
&lt;p&gt;Another thing is that you can “only” do a 1:1 relationship between publisher and consumer when using 
data queues. You can add multiple consumer to a data queue but a message will only be sent to one 
consumer, not to all. This means you cannot do something like 
&lt;a href=&#34;https://dzone.com/articles/comparing-publish-subscribe-messaging-and-message&#34;&gt;pub&amp;#x2F;sub&lt;/a&gt;, 1:n 
relationship where you publish a message and each subscribed consumer gets the same message.&lt;/p&gt;
&lt;h2 id=&#34;Message-Encoding&#34;&gt;&lt;a href=&#34;#Message-Encoding&#34; class=&#34;headerlink&#34; title=&#34;Message Encoding&#34;&gt;&lt;/a&gt;Message Encoding&lt;/h2&gt;&lt;p&gt;With the specification version 1.0 we were tied to sending messages in ASCII character encoding.
Today we can send the data in more or less any encoding supported by the platform. The 
implementation of the client allows to set the default character encoding with the following line:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;stomp_setContentType(client : &amp;#x27;application/json;charset=utf-8&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If nothing else is passed to the &lt;code&gt;stomp_command_send&lt;/code&gt; procedure everything will be encoded with 
the previously set default encoding. If we have a message which should be encoded differently we 
can specify a different MIME type (and character set) on the &lt;code&gt;stomp_command_send&lt;/code&gt; procedure.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;stomp_command_send(client : &amp;#x27;/queue/ticker&amp;#x27; : &amp;#x27;&amp;#123; &amp;quot;id&amp;quot; : 358 &amp;#125;&amp;#x27; : *omit : &amp;#x27;application/json;charset=utf-16&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;You don’t have to worry about how to convert the data to the desired character set. This is 
automatically done by the client. You just pass the data encoded in the CCSID of the current job 
and set the desired character set (added as a character set attribute of the MIME Type).&lt;/p&gt;
&lt;p&gt;The STOMP Client project comes with some predefined constants for the most often used MIME types, 
see MIME types in the &lt;a href=&#34;https://iledocs.rpgnextgen.com/&#34;&gt;ILEDocs&lt;/a&gt; documentation.&lt;/p&gt;
&lt;p&gt;A list of supported character sets and their mapping to a CCSID is available in the 
&lt;a href=&#34;https://bitbucket.org/m1hael/stomp/wiki/Supported%20Character%20Sets&#34;&gt;wiki&lt;/a&gt; of the project.&lt;/p&gt;
&lt;h2 id=&#34;Binary-Data&#34;&gt;&lt;a href=&#34;#Binary-Data&#34; class=&#34;headerlink&#34; title=&#34;Binary Data&#34;&gt;&lt;/a&gt;Binary Data&lt;/h2&gt;&lt;p&gt;Even though STOMP is a text oriented protocol with the new specification you can also send binary 
very easily.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-s data pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;data = loadBinaryData();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;stomp_command_send(client : &amp;#x27;/queue/binaries&amp;#x27; : data : *omit : STOMP_MIME_BINARY);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;This assumes that the data is null-terminated. If you want to send data which may include a x’00’ 
value you need to set the message data length (4th parameter) like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-s data pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-c DATA_SIZE 1024;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;data = loadBinaryData(DATA_SIZE);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;stomp_command_send(client : &amp;#x27;/queue/binaries&amp;#x27; : data : DATA_SIZE : STOMP_MIME_BINARY);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;h2 id=&#34;Logging&#34;&gt;&lt;a href=&#34;#Logging&#34; class=&#34;headerlink&#34; title=&#34;Logging&#34;&gt;&lt;/a&gt;Logging&lt;/h2&gt;&lt;p&gt;The first version of the STOMP client used the great 
&lt;a href=&#34;https://tools400.de/English/Freeware/Service_Programs/Log4rpg/log4rpg.html&#34;&gt;Log4RPG&lt;/a&gt; package 
from Thomas Raddatz for getting optional output for debugging purposes. This worked very well 
and was very flexible. But it also introduced some problems when it comes to deployment. 
Log4RPG depends on the two service programs from &lt;a href=&#34;https://tools400.de/&#34;&gt;tools400.de&lt;/a&gt;, 
&lt;a href=&#34;https://tools400.de/English/Freeware/Service_Programs/IFS/ifs.html&#34;&gt;IFS&lt;/a&gt; and 
&lt;a href=&#34;https://tools400.de/English/Freeware/Service_Programs/Basics/basics.html&#34;&gt;Basics&lt;/a&gt;. Among 
those projects Log4RPG and Basics are rather heavy weight and offer a great deal of functionality 
which we don’t use in the STOMP client project. We just need to output some messages to the 
job log for debugging purposes. So now we got an extremely simplified logging which can be 
activated by setting the environment variable &lt;code&gt;STOMP_DEBUG&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ADDENVVAR STOMP_DEBUG 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: The environment variable is queried at the initialization of the service program. A service 
program is only loaded once in the activation group. If you want to change the logging you need to 
start the client in a new activation group.&lt;/p&gt;
&lt;h2 id=&#34;Installation&#34;&gt;&lt;a href=&#34;#Installation&#34; class=&#34;headerlink&#34; title=&#34;Installation&#34;&gt;&lt;/a&gt;Installation&lt;/h2&gt;&lt;p&gt;The STOMP projects relies on three other projects: Linked List, Message and libtree. We could
build the dependencies first and then build the STOMP client itself but this is very tedious.&lt;/p&gt;
&lt;p&gt;Luckily there is a much easier solution: &lt;a href=&#34;https://bitbucket.org/m1hael/iPKG&#34;&gt;iPKG&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I have already built all dependencies and the STOMP client and created iPKG compatible packages.
So we can just use the package manager and install everything with some simple commands on the
5250 command line.&lt;/p&gt;
&lt;p&gt;To install the dependencies just execute the following commands:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install linkedlist&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ipkg install message&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ipkg install libtree&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;To install the STOMP client just execute the following command:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install stomp&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;We also need the STOMP copybooks for the prototypes, constants and templates.
These are stored as stream files. By default these will be placed into &lt;code&gt;/usr/local/include&lt;/code&gt;.
We can change that by specifying a different location on the &lt;code&gt;ipkg&lt;/code&gt; command:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;ipkg install &amp;#x27;stomp-devel&amp;#x27; loc(&amp;#x27;/home/mihael/include&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Much easier than compiling everything yourself.&lt;/p&gt;
&lt;h2 id=&#34;Examples&#34;&gt;&lt;a href=&#34;#Examples&#34; class=&#34;headerlink&#34; title=&#34;Examples&#34;&gt;&lt;/a&gt;Examples&lt;/h2&gt;&lt;p&gt;The source code repository has an &lt;a href=&#34;https://bitbucket.org/m1hael/stomp/src/master/examples/&#34;&gt;examples&lt;/a&gt;
folder which contains some basic examples on how to use the STOMP service program.&lt;/p&gt;
&lt;h2 id=&#34;Wrap-Up&#34;&gt;&lt;a href=&#34;#Wrap-Up&#34; class=&#34;headerlink&#34; title=&#34;Wrap Up&#34;&gt;&lt;/a&gt;Wrap Up&lt;/h2&gt;&lt;p&gt;There is a ton of additional features these message queuing systems have beyond a simple data queue. 
And having an additional system doesn’t mean it cannot run on IBM i. ActiveMQ Classic and ActiveMQ 
Artemis (the successor to the classic version) are both written in Java and can easily run on IBM i.&lt;/p&gt;
&lt;p&gt;It would be great if you could post some feedback. I am very interested which message queueing
system you are using the STOMP client with and if it worked out of the box (some projects don’t 
adhere so strictly to the spec as others).&lt;/p&gt;
&lt;p&gt;And what about speed?! In some small tests I was able to send 1000 messages from one client in 215 
milliseconds. I think that is not such a bad value for starters. How fast is your system?&lt;/p&gt;
&lt;p&gt;Happy stomping!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2021/05/15/2021-05-15-ileastic-tls/</guid>
            <title>ILEastic Tutorial - Thread Local Storage</title>
            <link>http://example.com/2021/05/15/2021-05-15-ileastic-tls/</link>
            <category>RPG</category>
            <pubDate>Sat, 15 May 2021 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;Another part of the mini series “ILEastic Tutorial : Tour of Champions” is available: &lt;strong&gt;Thread Local Storage&lt;/strong&gt;. &lt;/p&gt;
&lt;p&gt;This part will cover what Thread Local Storage is and how it can be used. It will open up a whole new load of 
possibilities from authentication to authorization and more specific setting of CORS headers.&lt;/p&gt;
&lt;p&gt;You can find it in the &lt;a href=&#34;https://champions.rpgnextgen.com/Web_Services/Thread_Local_Storage.html&#34;&gt;&lt;em&gt;Web Service&lt;/em&gt;&lt;/a&gt; section of the tutorial.&lt;/p&gt;
&lt;p&gt;If you have an idea what topic I should also cover in this series just drop me a mail.&lt;/p&gt;
&lt;p&gt;Happy microserving!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2021/05/13/2021-05-13-ileastic-plugins-routeid/</guid>
            <title>ILEastic Tutorial &amp;colon; Plugins - Route Id</title>
            <link>http://example.com/2021/05/13/2021-05-13-ileastic-plugins-routeid/</link>
            <category>RPG</category>
            <pubDate>Thu, 13 May 2021 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;This is a little update on the mini series “ILEastic Tutorial : Tour of Champions”. Last time
I covered plugins, those “special” procedures which are executed on the exit points of the 
ILEastic request handling workflow.&lt;/p&gt;
&lt;p&gt;There is another little feature which is also part of the plugins chapter of the tutorial: Route Id.&lt;/p&gt;
&lt;p&gt;We can add an id to the registration of a route &amp;#x2F; end point. By doing this we can identify the route
ILEastic is using for the request from inside a plugin. This allows us to further remove code for 
&lt;a href=&#34;https://en.wikipedia.org/wiki/Cross-cutting_concern&#34;&gt;cross-cutting concerns&lt;/a&gt; from the end points 
and put them centrally into the plugins.&lt;/p&gt;
&lt;h3 id=&#34;Example&#34;&gt;&lt;a href=&#34;#Example&#34; class=&#34;headerlink&#34; title=&#34;Example&#34;&gt;&lt;/a&gt;Example&lt;/h3&gt;&lt;p&gt;We want to set different CORS headers for each route and not just add “allow all” HTTP headers to
the response.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How do we know which end point is used for the request?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Very simple! Just query the subfield &lt;code&gt;routeId&lt;/code&gt; of the &lt;code&gt;il_request&lt;/code&gt; data structure. It will contain
the string we have passed on the &lt;code&gt;il_addRoute&lt;/code&gt; call (last parameter).&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;il_addRoute(config : %paddr(list) : IL_GET : &amp;#x27;/api/champion&amp;#x27; : *omit : &amp;#x27;champion_list&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc list;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    dcl-pi *n ind;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        request  likeds(IL_REQUEST);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        response likeds(IL_RESPONSE);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    end-pi;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    if (request.routeId = &amp;#x27;champion_list&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    endif;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now we can differentiate between the different end points in the plugin procedures.&lt;/p&gt;
&lt;p&gt;You can also find this information in the &lt;a href=&#34;https://champions.rpgnextgen.com/Web_Services/Plugins.html&#34;&gt;&lt;em&gt;Web Service&lt;/em&gt;&lt;/a&gt; section of the tutorial.&lt;/p&gt;
&lt;p&gt;If you have an idea what topic I should also cover in this series just drop me a mail.&lt;/p&gt;
&lt;p&gt;Happy microserving!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2021/05/01/2021-05-01-ileastic-plugins/</guid>
            <title>ILEastic Tutorial - Plugins</title>
            <link>http://example.com/2021/05/01/2021-05-01-ileastic-plugins/</link>
            <category>RPG</category>
            <pubDate>Sat, 01 May 2021 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;This is the second part of my mini series “ILEastic Tutorial : Tour of Champions”. Today I will
cover ILEastic plugins. In Java you would call them “Servlet Filter”. They intercept a request
before any routing to an endpoint&amp;#x2F;servlet has been done. It is a great way to extend the functionality
of your service.&lt;/p&gt;
&lt;p&gt;You can find it in the &lt;a href=&#34;https://champions.rpgnextgen.com/Web_Services/Plugins.html&#34;&gt;&lt;em&gt;Web Service&lt;/em&gt;&lt;/a&gt; section of the tutorial.&lt;/p&gt;
&lt;p&gt;If you have an idea what topic I should also cover in this series just drop me a mail.&lt;/p&gt;
&lt;p&gt;Happy microserving!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2021/04/25/2021-04-25-toc/</guid>
            <title>ILEastic Tutorial - Tour of Champions</title>
            <link>http://example.com/2021/04/25/2021-04-25-toc/</link>
            <category>RPG</category>
            <pubDate>Sun, 25 Apr 2021 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;This will be a series of little articles which show how to implement a microservice project on IBM i. 
I will start from a blank (or template) project and work my way through the different parts of the 
application. I will be using tools like git, bash, ssh, make to cover my basic needs for a development 
environment. As we are on IBM i I will use the native microservice framework &lt;a href=&#34;https://github.com/sitemule/ILEastic&#34;&gt;ILEastic&lt;/a&gt; 
for the web services, my favorite RPG editor &lt;a href=&#34;https://miworkplace.com/&#34;&gt;MiWorkplace&lt;/a&gt; ;-) for editing
RPG and &lt;a href=&#34;https://dbeaver.io/&#34;&gt;DBeaver&lt;/a&gt; for all stuff related to SQL.&lt;/p&gt;
&lt;p&gt;The content of the series is already available on the new website 
&lt;a href=&#34;https://champions.rpgnextgen.com/&#34;&gt;https://champions.rpgnextgen.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Each part of the series will be appended to the content of this website. It currently covers the basics 
like setting it all up, database, service programs and the first web service. Later parts of the series 
will cover things like SQL transactions in a multi threaded environment, authentication (Basic Auth and 
OAuth 2.0), embedding a template engine, error handling, administration, configuration provisioning and 
load balancing. Perhaps you have some other topics related to this. Please let me know, perhaps I can 
also cover that.&lt;/p&gt;
&lt;p&gt;I hope you enjoy the ride and realize that RPG is one of the best options for web services on IBM i 
because you have access to the &lt;em&gt;whole&lt;/em&gt; IBM i platform &lt;em&gt;natively&lt;/em&gt; and &lt;em&gt;directly&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Keep it simple! Use your mind!&lt;/p&gt;
&lt;p&gt;Happy microserving!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2021/04/03/2021-04-03-iledash/</guid>
            <title>ILEDash - First Alpha Release</title>
            <link>http://example.com/2021/04/03/2021-04-03-iledash/</link>
            <category>RPG</category>
            <pubDate>Sat, 03 Apr 2021 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;I have been developing web services with &lt;a href=&#34;https://github.com/sitemule/ILEastic&#34;&gt;ILEastic&lt;/a&gt;
for some time now and the framework is really great in what it does and provides. It handles
all the HTTP protocol stuff for you. It supports things like a plugin system, Basic Auth, 
JWT, JSON, request routing out of the box with a developer friendly API (documented with 
ILEDocs and available at &lt;a href=&#34;http://iledocs.rpgnextgen.com/&#34;&gt;iledocs.rpgnextgen.com&lt;/a&gt;. As a 
framework it really does not have to hide behind those microservice frameworks from other 
programming languages.&lt;/p&gt;
&lt;p&gt;What it does not provide is a good way to manage those web services. And it doesn’t have to.
Its main focus is microservices via HTTP. &lt;/p&gt;
&lt;p&gt;In a &lt;a href=&#34;https://blog.rpgnextgen.com/blog/2020/10/04/web-services-setup-with-ileastic&#34;&gt;previous post&lt;/a&gt;
I already mentioned how one could manage ILEastic web services with native IBM i tools. For
a couple of services this will probably suffice. But if you have dozens of web services you
can easily lose the overview.&lt;/p&gt;
&lt;p&gt;This is the moment where you need some software which helps you to manage your services 
and provides you with a good overview over your services.&lt;/p&gt;
&lt;p style=&#34;text-align: center; font-size: 200%; margin-top: 35px; margin-bottom: 35px;&#34;&gt;&lt;b&gt;ILEDash to the rescue!&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://rpgnextgen.com/images/iledash-intro.png&#34; alt=&#34;ILEDash Overview&#34;&gt;&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;&lt;a href=&#34;https://iledash.rpgnextgen.com/&#34;&gt;ILEDash&lt;/a&gt; is a new project from me and myself. You specify
your web services in the web application of ILEDash and not just the programs itself but 
you can also specify the configuration for each service. You stop and start a service with 
just a button click. Running multiple instances of a web service is also no problem if 
you need to cover a higher volume of requests (f. e. in combination with a load balancer 
like the Apache HTTP server on IBM i which I will cover in another post).&lt;/p&gt;
&lt;br/&gt;

&lt;h2 id=&#34;Technology&#34;&gt;&lt;a href=&#34;#Technology&#34; class=&#34;headerlink&#34; title=&#34;Technology&#34;&gt;&lt;/a&gt;Technology&lt;/h2&gt;&lt;p&gt;The backend is powered by native RPG programs using the ILEastic microservice framework.
The frontend uses Angular for providing a clean and functional UI for managing your services.&lt;/p&gt;
&lt;p&gt;And because of this technology mix you can run it all on your IBM i. No other servers 
needed. ILEastic is native ILE and the web frontend can be hosted with the Apache HTTP 
Server on your IBM i server.&lt;/p&gt;
&lt;br/&gt;

&lt;h2 id=&#34;Open-Source&#34;&gt;&lt;a href=&#34;#Open-Source&#34; class=&#34;headerlink&#34; title=&#34;Open Source&#34;&gt;&lt;/a&gt;Open Source&lt;/h2&gt;&lt;p&gt;There is currently so much buzz about Open Source on IBM i and I never hear anything 
about the funtastic and fantastic Open Source ILE projects. Perhaps this project will 
get the ball rolling, meaning this project is Open Source.&lt;/p&gt;
&lt;br/&gt;

&lt;h2 id=&#34;Open-Alpha&#34;&gt;&lt;a href=&#34;#Open-Alpha&#34; class=&#34;headerlink&#34; title=&#34;Open Alpha&#34;&gt;&lt;/a&gt;Open Alpha&lt;/h2&gt;&lt;p&gt;The project is currently in its alpha stage. Meaning it generally works but there are 
probably some edge cases which need some more attention. Everybody who wants to give 
it a try is welcome. Please send me a mail at &lt;a href=&#34;mailto:&amp;#x6d;&amp;#x69;&amp;#x68;&amp;#97;&amp;#x65;&amp;#x6c;&amp;#64;&amp;#x72;&amp;#112;&amp;#103;&amp;#110;&amp;#101;&amp;#x78;&amp;#x74;&amp;#103;&amp;#101;&amp;#x6e;&amp;#46;&amp;#99;&amp;#111;&amp;#109;&#34;&gt;&amp;#x6d;&amp;#x69;&amp;#x68;&amp;#97;&amp;#x65;&amp;#x6c;&amp;#64;&amp;#x72;&amp;#112;&amp;#103;&amp;#110;&amp;#101;&amp;#x78;&amp;#x74;&amp;#103;&amp;#101;&amp;#x6e;&amp;#46;&amp;#99;&amp;#111;&amp;#109;&lt;/a&gt; if you are interested
and you can find the software in the &lt;a href=&#34;https://iledash.rpgnextgen.com/index.php?content=download&#34;&gt;download section&lt;/a&gt; 
on the website. You can find the installation instructions in the 
&lt;a href=&#34;https://iledash.rpgnextgen.com/index.php?content=install&#34;&gt;installation section&lt;/a&gt;
on the website.&lt;/p&gt;
&lt;br/&gt;

&lt;h2 id=&#34;Documentation&#34;&gt;&lt;a href=&#34;#Documentation&#34; class=&#34;headerlink&#34; title=&#34;Documentation&#34;&gt;&lt;/a&gt;Documentation&lt;/h2&gt;&lt;p&gt;This is one thing the projects needs to massively improve because the documentation is basically
non existent. This will be one of my next tasks to work on.&lt;/p&gt;
&lt;p&gt;But for starters you really just need to start the subsystem. The necessary jobs will be started 
automatically as they are configured as autostart jobs in the subsystem description.&lt;/p&gt;
&lt;p&gt;If you need any help just contact me.&lt;/p&gt;
&lt;br/&gt;

&lt;h2 id=&#34;Feedback&#34;&gt;&lt;a href=&#34;#Feedback&#34; class=&#34;headerlink&#34; title=&#34;Feedback&#34;&gt;&lt;/a&gt;Feedback&lt;/h2&gt;&lt;p&gt;Currently most features I need are already implemented. It would be great to get 
some feedback on your experience with ILEDash and your thoughts on what is good 
or bad about the project and what you are missing.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;I am looking forward to hear from you all.&lt;/p&gt;
&lt;p&gt;Happy Easter Day!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2021/02/28/2021-02-28-errorhandling/</guid>
            <title>Error Handling</title>
            <link>http://example.com/2021/02/28/2021-02-28-errorhandling/</link>
            <category>RPG</category>
            <pubDate>Sun, 28 Feb 2021 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;I think this is a difficult topic and many people will 
obviously have their own solution to this problem. And this is not really an RPG
only topic. This problem exists in every programming language (I have encountered
so far).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What to do when an error can occur?&lt;/p&gt;
&lt;p&gt;Should I catch&amp;#x2F;monitor the error? Should I log the error? Should I do anything?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And especially the last question is the one where most people will say: “Of course
you need to do something!” But this is not true at all. If you cannot handle the 
error in the location you are (in your program or stack) then you probably should 
not do anything and let the error &lt;em&gt;“bubble up”&lt;/em&gt; to the next level in the call stack 
where it might be handled in a better way.&lt;/p&gt;
&lt;h2 id=&#34;Log-and-Throw&#34;&gt;&lt;a href=&#34;#Log-and-Throw&#34; class=&#34;headerlink&#34; title=&#34;Log and Throw&#34;&gt;&lt;/a&gt;Log and Throw&lt;/h2&gt;&lt;p&gt;This is a common thing we have learned is a “NoNo”. But this is where our IBM i 
platform (especially the ILE environment) is different than other platforms: &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Everything is already logged in the job log!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Everything?&lt;/em&gt; Probably not. But it is enough to say that we don’t need any additional 
logging strategy for errors. That does not mean that we never need any additional 
logging at all. More log statements may better show the flow inside the program
and other log frameworks may use another medium beside the job log. But that is 
another topic.&lt;/p&gt;
&lt;p&gt;But &lt;em&gt;“Log and throw”&lt;/em&gt; cannot be excluded on IBM i as every escape message is 
automatically logged in the job log and we may want to communicate to the caller 
a specific message or condition. So we may &lt;em&gt;rethrow&lt;/em&gt; the caught escape message 
with a specific message from our own message file.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://rpgnextgen.com/images/error-handling.jpg&#34; alt=&#34;Error Handling&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;Sending-an-Escape-Message&#34;&gt;&lt;a href=&#34;#Sending-an-Escape-Message&#34; class=&#34;headerlink&#34; title=&#34;Sending an Escape Message&#34;&gt;&lt;/a&gt;Sending an Escape Message&lt;/h2&gt;&lt;p&gt;Sending a message from a message file does not have to be complicated. It can 
be as easy as calling a single procedure.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;message_file_escape(*omit : &amp;#x27;DMOMSGFILE&amp;#x27; : &amp;#x27;DMO0010&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now the caller can use a monitor block to catch that escape message and analyze it.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;/include psds.rpginc&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;/include qusec.rpginc&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-ds errorCode likeds(qusec) inz;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;monitor;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  demo_persist(data);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;on-error;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  if (exception = &amp;#x27;DMO0010&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    sendValidationErrorResponse();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  else;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    // cannot handle any other error here =&amp;gt; send it to the next call stack entry&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    message_resendEscapeMessage(&amp;#x27;&amp;#x27; : errorCode);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  endif;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;endmon;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;You can find the procedures about messages in the &lt;a href=&#34;https://bitbucket.org/m1hael/message&#34;&gt;message&lt;/a&gt; 
project and the API documentation at my &lt;a href=&#34;http://iledocs.rpgnextgen.com/&#34;&gt;ILEDocs&lt;/a&gt; site.&lt;/p&gt;
&lt;h2 id=&#34;Escape-Messages-only-for-real-Errors&#34;&gt;&lt;a href=&#34;#Escape-Messages-only-for-real-Errors&#34; class=&#34;headerlink&#34; title=&#34;Escape Messages only for real Errors&#34;&gt;&lt;/a&gt;Escape Messages only for real Errors&lt;/h2&gt;&lt;p&gt;When you want to communicate something back to the caller which is not an error 
then don’t use an escape message. It is way more effecient to use something like 
a constant as a return value than an escape message to do that. Handling escape 
messages is much more expensive than just checking a value.&lt;/p&gt;
&lt;h2 id=&#34;Documentation&#34;&gt;&lt;a href=&#34;#Documentation&#34; class=&#34;headerlink&#34; title=&#34;Documentation&#34;&gt;&lt;/a&gt;Documentation&lt;/h2&gt;&lt;p&gt;And document what the user can expect! If a program or procedure sends an escape 
message in a certain situation then write it down so that the user of the program
or procedure can handle it if possible (or send it further up the stack).&lt;/p&gt;
&lt;p&gt;The same goes for web service endpoints: document what the web client can expect!&lt;/p&gt;
&lt;p&gt;Happy error handling!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2021/02/27/2021-02-27-message-1.2.0/</guid>
            <title>Message Service Program</title>
            <link>http://example.com/2021/02/27/2021-02-27-message-1.2.0/</link>
            <pubDate>Sat, 27 Feb 2021 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;The &lt;a href=&#34;https://bitbucket.org/m1hael/message&#34;&gt;message&lt;/a&gt; project has been extended by some procedures.
Now you can also use messages from message files.&lt;/p&gt;
&lt;p&gt;You can get the message service program from the save file in the 
&lt;a href=&#34;https://bitbucket.org/m1hael/message/downloads/&#34;&gt;download section&lt;/a&gt; of the project or fetch the 
source from the git repository. The documentation has been uploaded to my 
&lt;a href=&#34;https://iledocs.rpgnextgen.com/&#34;&gt;ILEDocs&lt;/a&gt; site.&lt;/p&gt;
&lt;p&gt;This also enables us to more easily communicate specific errors to the caller.
How to do this and where it can be very useful will be displayed in another 
blog post.&lt;/p&gt;
&lt;p&gt;Happy messaging!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2021/01/01/2021-01-01-modernization/</guid>
            <title>Modernization</title>
            <link>http://example.com/2021/01/01/2021-01-01-modernization/</link>
            <category>RPG</category>
            <pubDate>Fri, 01 Jan 2021 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;I think there is not one topic in the IBM i community which has been talked so 
much about as modernization. And here I am also talking about it :) .&lt;/p&gt;
&lt;p&gt;The modernization drum has been beaten for as long as I can think of and every 
time I hear about it it is just about putting lipstick on a pig and moving things 
to the web with the least effort possible no matter what the result looks like 
or what the underlying programs still look like. But I won’t handle the frontend 
or web topic here. &lt;/p&gt;
&lt;p&gt;From the top of my head I only remember a single article where they have used a 
staged approach with first bringing it all to the web with a screen converting 
product but then also doing the next step and doing a full rework of the whole 
application. Most people are only doing step one but missing the really important 
step two. &lt;/p&gt;
&lt;p&gt;But this post is about the backend accessing the database and the first thing I 
hear is&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Use SQL views! Abstract the database tables away and use views!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In some cases it probably makes sense. &lt;em&gt;BUT NOT IN ALL!!! Come on! Use your mind!&lt;/em&gt; 
If the database table has already names that make sense, if there is no additional 
logic to be &lt;em&gt;captured&lt;/em&gt; in views … if there are no not used columns or &lt;em&gt;misused&lt;/em&gt; 
columns … (fill in some more conditions) … then why should I use a view?!?! &lt;/p&gt;
&lt;p&gt;What I also hear and read a lot is &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Make a database access layer!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In my opinion this is something which has been swept into the IBM i community 
from other platforms where you don’t have such a good database access method 
like we have in RPG where database access is built into from the start. On 
other platforms you don’t have native database support at all. On these platforms 
database access is much more complex and it all comes with additional software 
libraries, keyword ORM. Some might say now&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hey, but doesn’t f. e. Java has JDBC and JPA?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yes. That is right. But that is something which doesn’t come with the stock JRE 
(ok … JDBC does come with standard JRE). The JPA interfaces come with the JEE 
Runtime and then you still have no implementation to work with. (I won’t cover 
JDBC here because this is not something you would like to work with in the first 
place when using Java and like to access the database). For JPA you need a JPA 
implementation (like &lt;a href=&#34;https://www.eclipse.org/eclipselink&#34;&gt;EclipseLink&lt;/a&gt; or 
&lt;a href=&#34;http://openjpa.apache.org/&#34;&gt;Apache OpenJPA&lt;/a&gt;) and additionally the 
database driver so how much built-in is it really? I don’t mean that it doesn’t 
work. I had developed and maintained a project for seven years which used JPA on 
an x86 based platform and it worked great (also much thanks to &lt;a href=&#34;https://www.osgi.org/&#34;&gt;OSGi&lt;/a&gt;, 
which will soon &lt;a href=&#34;https://blog.osgi.org/2020/10/announcement-of-transition-to-eclipse.html&#34;&gt;move under the Eclipse umbrella&lt;/a&gt;
, and &lt;a href=&#34;http://karaf.apache.org/&#34;&gt;Apache Karaf&lt;/a&gt;) but my opinion is that it is not 
as much built-in as the database support is built into the RPG language.&lt;/p&gt;
&lt;p&gt;So coming back to the database layer … I don’t think that you need an additional 
layer in RPG that does nothing else but database access. So rather than having 
like two or sometimes three layers for justing getting the data to the next level 
(database layer, business layer, transformatiton layer for transforming from 
internal model to external model) I like to put everything in a single layer. I 
don’t say that you never need any additional layer but only a single one … but 
use your mind. Start with the simple approach and look how far it takes you and 
at what point it doesn’t suffice. And how much more does it cost to have a more 
complex approach and is it worth it? What extra value does an extra layer gives 
you which you &lt;em&gt;really&lt;/em&gt; need?&lt;/p&gt;
&lt;p&gt;The simple approach for me would be to optional make SQL views if needed or just 
plainly use the database tables. Don’t blindly make SQL indexes. Use visual 
explain to check where you need an index and where the database engine doesn’t 
use any index you created in good faith. Start with one layer to access the 
database and make your data available to any layer which presents the data to 
the frontend. Put a data structure for the data and necessary prototoypes in a 
copybook to let the next layer interface with the &lt;em&gt;data access&lt;/em&gt; layer. Don’t let 
any database specifics trickle through to the next layer. By this it doesn’t 
matter if the next layer is a 5250 program, any other service program or a web 
service done with &lt;a href=&#34;https://github.com/sitemule/ILEastic&#34;&gt;ILEastic&lt;/a&gt;. And by doing 
this it doesn’t matter if some or all of the data comes from the local database 
or if some or all of it is pulled from some other (web) service. &lt;/p&gt;
&lt;p&gt;It does not matter! … and it shouldn’t!&lt;/p&gt;
&lt;p&gt;So if you really have read the article this far you probably already got the 
message I try to bring to you with this post&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Don’t always generalize!!! Use your mind!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Don’t make a database access layer just for the case of having such a layer 
especially if it doesn’t make anything but a straight read and write without 
doing anything else. For some data I don’t even write any layer but let the 
last layer access the database directly because the data doesn’t need any 
additional management and is extremely simple and rather static and in some 
cases also read-only. In this case the database itself is the interface to the 
data. Is this the best way for all cases? NO!!! But you need to decide that 
from case to case. There is no one-size-fits-all!&lt;/p&gt;
&lt;p&gt;So the overall message is&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Use your mind!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Have a great new year!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2020/12/31/2020-12-31-dms/</guid>
            <title>Demonstrate My Style</title>
            <link>http://example.com/2020/12/31/2020-12-31-dms/</link>
            <category>RPG</category>
            <pubDate>Thu, 31 Dec 2020 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;There are many web sites which discuss the technical parts of RPG covering the 
latest bifs and opcodes and they are great for looking up specific things about RPG.&lt;/p&gt;
&lt;p&gt;But when you are new to RPG and perhaps programming in general you actually might
not know what a “good” programming style is and you may have not discovered or 
developed your own coding style.&lt;/p&gt;
&lt;p&gt;So here I want to give you something to start with : &lt;a href=&#34;https://dms.rpgnextgen.com/&#34;&gt;Demonstrate My Style&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is not something written in stone and not a general rule which you &lt;em&gt;have to&lt;/em&gt;
follow but my personal preferences at the time of writing which you may use as a 
starting point to develop your own style.&lt;/p&gt;
&lt;p&gt;For me coding is something between craftsmanship and art. It is not just about
performance and business values. And having something which just barely works is 
not something I pursue.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2020/12/05/2020-12-03-tls-in-ileastic/</guid>
            <title>Thread Local Storage and ILEastic</title>
            <link>http://example.com/2020/12/05/2020-12-03-tls-in-ileastic/</link>
            <category>RPG</category>
            <pubDate>Sat, 05 Dec 2020 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;blockquote&gt;
&lt;p&gt;Thread Local Storage (TLS) is a widely used programming method that uses static 
or global memory local to a thread. &amp;mdash; &lt;a href=&#34;https://en.wikipedia.org/wiki/Thread-local_storage&#34;&gt;Thread Local Storage on Wikipedia&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So TLS is all about having a storage area per thread. This is not much of a concern 
for most RPG developers. Most of us are working with jobs and not directly with 
threads. But if you take a look at the details of a job (&lt;code&gt;DSPJOB OPTION(*THREAD)&lt;/code&gt;) you 
will see that your job has one thread, the main thread. And all your programs are 
executed in this main thread.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;So what good is TLS for you?&lt;/em&gt; It can help you even if you have only your main thread. 
You can put something in that storage and at a later time you can retrieve it. This 
is really helpful if you have something you need to save at some point in your 
programs (f. e. at login) and don’t need it 99% of the time. But you don’t want to 
pass this data around as a parameter to all programs or procedures. So putting that 
in the TLS and retrieving it 10 entries deeper in your callstack can make things easier.&lt;/p&gt;
&lt;p&gt;But many readers will now say &lt;em&gt;“Hey, we already got that covered. We got LDA and GDA for this.”&lt;/em&gt;.
Yes, you can use LDA for this and it is a nice for storing data at the scope of the job. 
But how much data can fit into the LDA? Not very much. And how often did you step onto 
the toes of another application storing data in the same LDA position, f. e. 3rd party
software?&lt;/p&gt;
&lt;h2 id=&#34;Implementation&#34;&gt;&lt;a href=&#34;#Implementation&#34; class=&#34;headerlink&#34; title=&#34;Implementation&#34;&gt;&lt;/a&gt;Implementation&lt;/h2&gt;&lt;p&gt;There are many ways how to implement TLS. A very easy implementation can be done in RPG. 
Yes … your eyes don’t betray you. It is good old RPG. And we even need only a single 
control option for this: &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;thread(*concurrent)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will make your RPG module thread safe. From the &lt;a href=&#34;https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/rzasd/threadconc.htm&#34;&gt;IBM Knowledge Center&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If THREAD(*CONCURRENT) is specified, then multiple threads can run in the module at 
the same time. By default, all the static storage in the module will be in thread-local 
storage, meaning that each thread will have its own copy of the static variables in 
the module, including compiler-internal variables. This allows multiple threads to run 
the procedures within the module at the same time and be completely independent of each other.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You don’t have to do any extra. Just put that control option into your module. Then 
declare a global variable and more or less you are already done. You can write some 
procedures to manage access to this variable. But that’s it!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;“But how is this better than using LDA?”&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For example you can use this in a multi-threaded and in a single-threaded environment. 
It doesn’t matter. LDA will not work in a multi-threaded environment or should I rather 
say it will have some side effects as each thread accesses the same data area. &lt;/p&gt;
&lt;p&gt;And as you implement it yourself it is totally up to you what features your TLS has. 
F. e. you can use a &lt;a href=&#34;https://github.com/sitemule/noxDB&#34;&gt;noxDB&lt;/a&gt; graph as your TLS. This 
means you can even store hierarchical data. Or have one graph branch for each part of 
your application, f. e. &lt;code&gt;/user&lt;/code&gt; for all your user data in the job. So you would query 
&lt;code&gt;/user/name&lt;/code&gt; for the name of the user and &lt;code&gt;/user/roles&lt;/code&gt; to get an array of roles the 
user has or &lt;code&gt;/user/email&lt;/code&gt; for his email address. No more toe stepping. It is totally 
up to the implementation.&lt;/p&gt;
&lt;p&gt;You can find a simple implementation here on &lt;a href=&#34;https://bitbucket.org/m1hael/threadlocal&#34;&gt;Bitbucket.org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;“But doesn’t a new service program instance gets created with each new activation group?”&lt;/em&gt; &lt;/p&gt;
&lt;p&gt;Yes. But if you specify a named activation group for this service program it will 
always be executed in the same named activation group and everything should be fine.&lt;/p&gt;
&lt;h2 id=&#34;TLS-and-ILEastic&#34;&gt;&lt;a href=&#34;#TLS-and-ILEastic&#34; class=&#34;headerlink&#34; title=&#34;TLS and ILEastic&#34;&gt;&lt;/a&gt;TLS and ILEastic&lt;/h2&gt;&lt;p&gt;&lt;a href=&#34;https://github.com/sitemule/ILEastic&#34;&gt;ILEastic&lt;/a&gt; is a microservice framework for creating 
web services in ILE languages, f. e. in RPG. Yes, your reading is just fine. No PASE or 
Java involved. Just some great ILE modules bound together in a nice service program. 
ILEastic creates a new thread for each request (as most web and application servers do today). &lt;/p&gt;
&lt;p&gt;So this is where our thread local storage comes into play.&lt;/p&gt;
&lt;p&gt;You can do some initialization of your TLS in a plugin which is registered on PREREQUEST. &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;il_addPlugin(config : %paddr(&amp;#39;web_util_tlsPlugin&amp;#39;) : IL_PREREQUEST);
&lt;/code&gt;&lt;/pre&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc web_util_tlsPlugin export;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-pi *n ind;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    request  likeds(IL_REQUEST);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    response likeds(IL_RESPONSE);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  end-pi;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s threadLocal pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s username varchar(100);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s tls pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  threadLocal = il_getThreadMem(request);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  // The ILEastic JWT plugin places the payload of the JWT under /ileastic/jwt/payload.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  // We don&amp;#x27;t have to worry about not having a value here because without a proper &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  // token or payload the request is returned with a 401 Unauthorized response and &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  // never reaches this line.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  username = jx_getStr(threadLocal : &amp;#x27;/ileastic/jwt/payload/username&amp;#x27; : &amp;#x27;unknown&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  tls = tls_getStorage();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  jx_setStr(tls : &amp;#x27;/user/name&amp;#x27; : username);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  return *on;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Means it will get executed before the request is handled by our end point &amp;#x2F; route procedure.
If you need to do some cleanup you can register a plugin on POSTRESPONSE.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;il_addPlugin(config : %paddr(&amp;#39;web_util_tlsFree&amp;#39;) : IL_POSTRESPONSE);
&lt;/code&gt;&lt;/pre&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc web_util_tlsFree export;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-pi *n ind;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    request  likeds(IL_REQUEST);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    response likeds(IL_RESPONSE);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  end-pi;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  tls_freeStorage();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  return *on;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;ILEastic has its own TLS. You can access it by calling &lt;code&gt;il_getThreadMem()&lt;/code&gt;. You 
get a noxDB graph which you can use as you wish. Only the branch &lt;code&gt;/ileastic&lt;/code&gt; is 
reserved for ILEastic itself.&lt;/p&gt;
&lt;p&gt;But you don’t want to have ILEastic as a dependency for every program or service 
program. Especially not those which have nothing to do with web services. So having 
ones own TLS is a much cleaner solution.&lt;/p&gt;
&lt;p&gt;Note: When registering plugins be aware that they will be called by ILEastic in the same 
      order you are registering them.&lt;/p&gt;
&lt;h2 id=&#34;Use-Case&#34;&gt;&lt;a href=&#34;#Use-Case&#34; class=&#34;headerlink&#34; title=&#34;Use Case&#34;&gt;&lt;/a&gt;Use Case&lt;/h2&gt;&lt;p&gt;A good use case for this would be the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You have created a web service with ILEastic.&lt;/li&gt;
&lt;li&gt;This web service uses a JWT token for authentication.&lt;/li&gt;
&lt;li&gt;ILEastic checks the token and puts the token data into its thread local storage by using the ILEastic JWT plugin.&lt;/li&gt;
&lt;li&gt;You use your own TLS plugin to store that token data (like user id, name, …) in your own TLS.&lt;/li&gt;
&lt;li&gt;Down the callstack you access your TLS to store in the database which user has created or changed that database entry.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;For example you can use this in a multi-threaded and in a single-threaded environment.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As I have said you can use the same TLS service program in your 5250 interactive 
and batch programs. You just need to initialize your TLS with the data from the 
current job. In our use case this would be the user name from the current
job. So if you now query &lt;code&gt;/user/name&lt;/code&gt; you still get a valid value, regardless of
the environment.&lt;/p&gt;
&lt;p&gt;Happy threading!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2020/10/11/2020-10-11-validation-list/</guid>
            <title>Working with Validation Lists</title>
            <link>http://example.com/2020/10/11/2020-10-11-validation-list/</link>
            <category>RPG</category>
            <pubDate>Sun, 11 Oct 2020 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;Probably not many of you have heard of validation lists on IBM i. So from the IBM
Knowledge Center&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Validation list objects provide a method for applications to securely store user authentication information.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Validation lists can store any data associated with a key. The good thing about a 
validation list is that you have the option to have the data one-way or two-way encrypted
which makes it an ideal option for storying authentication data. One other reason
for using validation lists instead of user profiles is that you don’t have to
create a whole user profile on the system when you only want to authenticate a
“user”.&lt;/p&gt;
&lt;p&gt;IBM has delivered a great option for authenticating “users” but failed on
delivering all tools needed for easily working with validation lists. It
has provided several APIs for accessing the validation list but failed on
providing higher level service programs and commands.&lt;/p&gt;
&lt;p&gt;But as the API is documented in the &lt;a href=&#34;https://www.ibm.com/docs/en/i/7.4&#34;&gt;IBM Knowledge Center&lt;/a&gt;
the IBM i community has all the information needed for creating those tools by 
themselves. And Carsten Flensburg did so and provides all the necessary code and 
information in his really good “APIs by Example” series and 
&lt;a href=&#34;https://apimymymy.wordpress.com/&#34;&gt;on his website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;MANY THANKS!!!&lt;/p&gt;
&lt;p&gt;The only thing you now need to do is download the code and compile it on your
machine. That can be a hurdle for one or the other and thus I have packaged
all the validation list commands, panel groups and other necessary objects
into one &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg&#34;&gt;iPKG&lt;/a&gt; package for easy installation. 
Now you can install all the validation list tools from Carsten Flensburg with a 
single command&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ipkg install vldl
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Can it be any easier?! No … i think not (at least not for 3rd party software).
You can find the packaged code on the &lt;a href=&#34;https://bitbucket.org/m1hael/cflensburg&#34;&gt;project web site&lt;/a&gt;. 
The iPKG packages can be found at &lt;a href=&#34;https://repo.rpgnextgen.com/&#34;&gt;https://repo.rpgnextgen.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy validating!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2020/10/04/2020-10-04-ileastic-setup/</guid>
            <title>Web Services Setup with ILEastic</title>
            <link>http://example.com/2020/10/04/2020-10-04-ileastic-setup/</link>
            <category>RPG</category>
            <pubDate>Sun, 04 Oct 2020 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;I have written some articles about writing web services with 
&lt;a href=&#34;https://github.com/sitemule/ILEastic&#34;&gt;ILEastic&lt;/a&gt; and
it is really as simple as already shown. And most of the time it is not any
easier in other languages. You have to do&amp;#x2F;check&amp;#x2F;consider the same things regardless
of the programming language.&lt;/p&gt;
&lt;p&gt;But what I didn’t cover so far was how to deploy and run the web service once
we have finished developing it. Deployment is really easy and it doesn’t take
a lot to cover that because with ILEastic your web service program will be a 
native IBM i object in the QSYS file system. You only need to put the 
compiled object in the library where you want to have your own objects.&lt;/p&gt;
&lt;p&gt;Setting up the web service to run when the system starts is another thing. And
there are probably many ways to skin the cat. I want to show you a simple one
(not necessarily the best one).&lt;/p&gt;
&lt;h2 id=&#34;Subsystem&#34;&gt;&lt;a href=&#34;#Subsystem&#34; class=&#34;headerlink&#34; title=&#34;Subsystem&#34;&gt;&lt;/a&gt;Subsystem&lt;/h2&gt;&lt;p&gt;I typically want to have my web services all in a separate subsystem. So we 
create a brand new subsystem for them.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CRTSBSD SBSD(MY_OBJ/WS) POOLS((1 *BASE)) TEXT(&amp;#39;Web Services&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That only creates a description of the subsystem. You can start it with the 
&lt;code&gt;STRSBS&lt;/code&gt; command.&lt;/p&gt;
&lt;h2 id=&#34;Class&#34;&gt;&lt;a href=&#34;#Class&#34; class=&#34;headerlink&#34; title=&#34;Class&#34;&gt;&lt;/a&gt;Class&lt;/h2&gt;&lt;p&gt;This class has nothing to do with a programming construct or concept. A class
defines how many resources a job can get, like processor time, memory and 
number of threads. Remember that ILEastic is a multi-threaded web framework so
we need to be able to create multiple threads in a job.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CRTCLS CLS(MY_OBJ/WS) TEXT(&amp;#39;Web Services&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;Job-Queue&#34;&gt;&lt;a href=&#34;#Job-Queue&#34; class=&#34;headerlink&#34; title=&#34;Job Queue&#34;&gt;&lt;/a&gt;Job Queue&lt;/h2&gt;&lt;p&gt;As jobs are entering a subsystem through a job queue we will need to create at
least one job queue and attach that to the subsystem. You can attach more than
one job queue to a subsystem.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CRTJOBQ JOBQ(MY_OBJ/WS) TEXT(&amp;#39;Web Services&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We need yet to attach the job queue to the subsystem.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ADDJOBQE SBSD(MY_OBJ/WS) JOBQ(MY_OBJ/WS) MAXACT(*NOMAX)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;Job-Description&#34;&gt;&lt;a href=&#34;#Job-Description&#34; class=&#34;headerlink&#34; title=&#34;Job Description&#34;&gt;&lt;/a&gt;Job Description&lt;/h2&gt;&lt;p&gt;The job description defines the environment of a job during its runtime in the
system. Things like printer, logs, library list, multi-threading capability,
which user profile is used for running the job and also which program is called
initially at job start.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CRTJOBD JOBD(MY_OBJ/WS_HELLO) JOBQ(MY_OBJ/WS) USER(WS) RQSDTA(&amp;#39;CALL PGM(HELLOWS)&amp;#39;) 
    INLLIBL(MY_OBJ SHARED ILEASTIC) ALWMLTTHD(*YES) TEXT(&amp;#39;Web Services : Hello World&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every job runs under a certain user profile. If it is an interactive job then
the user has logged himself into the system. A submitted job uses the same 
user profile as the original job which has done the submit. But if a job is 
autostarted when the subsystem is started there is no job from where to inherit
the user profile. So you need to specify a user profile in the job description 
and &lt;em&gt;don’t&lt;/em&gt; use the default value &lt;em&gt;&amp;ast;RQD&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Note: When you are already using &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg&#34;&gt;iPKG&lt;/a&gt; for 
      installing 3rd party open source software you will probably want to add 
      the IPKG library to the library list of the job description.&lt;/p&gt;
&lt;h2 id=&#34;Autostart-Job&#34;&gt;&lt;a href=&#34;#Autostart-Job&#34; class=&#34;headerlink&#34; title=&#34;Autostart Job&#34;&gt;&lt;/a&gt;Autostart Job&lt;/h2&gt;&lt;p&gt;The web service should automatically start when the subsystem is started. This
can be achieved with autostart jobs. You need to specify which job description
should be used with the autostart job. This also determines which web service 
(program) is started on job start, see request data of the job description.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ADDAJE SBSD(MY_OBJ/WS) JOB(WS_HELLO) JOBD(MY_OBJ/WS_HELLO)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can see now that we need a job description for every web service we want to
automatically start at subsystem start.&lt;/p&gt;
&lt;h2 id=&#34;Routing-Entry&#34;&gt;&lt;a href=&#34;#Routing-Entry&#34; class=&#34;headerlink&#34; title=&#34;Routing Entry&#34;&gt;&lt;/a&gt;Routing Entry&lt;/h2&gt;&lt;p&gt;If you now start your new subsystem you will see that there is nothing to see.
No jobs are running in your subsystem. That is because every job is routed 
internally in the subsystem to a program which executes the request data of
the job (from the job description). Depending on the routing you can specify
that a job in the subsystem has different resource limits by assigning a 
certain class object (remember the previously created class object).&lt;/p&gt;
&lt;p&gt;The request data is just text for the subsystem on which to route to a
specifiy program with certain resource limits (class).&lt;/p&gt;
&lt;p&gt;To make things easy we use the QCMD program to excute our request data 
(which is a simple CALL) and use the previously create class object for
every started job.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ADDRTGE SBSD(MY_OBJ/WS) SEQNBR(9999) CMPVAL(*ANY) PGM(QCMD) CLS(MY_OBJ/WS) MAXACT(100)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If no routing entry matches the request data of a job then the job is just dropped.
I have yet to figure out where to find any log entry about the dropped job.&lt;/p&gt;
&lt;h2 id=&#34;Restart-Web-Services&#34;&gt;&lt;a href=&#34;#Restart-Web-Services&#34; class=&#34;headerlink&#34; title=&#34;Restart Web Services&#34;&gt;&lt;/a&gt;Restart Web Services&lt;/h2&gt;&lt;p&gt;Restarting a web service is really simple because you just need to kill the old
job submit a new one with the corresponding job description and that’s it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SBMJOB JOBD(MY_OBJ/WS_HELLO) USER(*JOBD) RQSDTA(*JOBD) CURLIB(*CRTDFT) INLLIBL(*JOBD)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can start your subsystem and have your web service say hello to the world! :)&lt;/p&gt;
&lt;p&gt;Happy setting up!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2020/07/22/2020-07-22-ileastic-config-2/</guid>
            <title>Dynamic Configuration with ILEastic</title>
            <link>http://example.com/2020/07/22/2020-07-22-ileastic-config-2/</link>
            <category>RPG</category>
            <pubDate>Wed, 22 Jul 2020 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;My last post was about sharing configuration in an &lt;a href=&#34;https://github.com/sitemule/ILEastic&#34;&gt;ILEastic&lt;/a&gt; 
web service. This is especially handy when you split your web service application into multiple modules.
This is not necessary but may be a good idea if you have many end points &amp;#x2F; routes so
that your modules don’t grow too big.&lt;/p&gt;
&lt;p&gt;But sometimes the configuration is dynamic and you can’t put it manually into a 
table which is later read from the web service application.&lt;/p&gt;
&lt;p&gt;Use case: You want to dynamically start new web service instances depending on the
utilization. So beforehand you don’t know how many instances&amp;#x2F;jobs you will start 
and each instance needs at least another port.&lt;/p&gt;
&lt;p&gt;Solution: You define a port range. You provide a configuration web service which
among other attributes contains a free port number from the port range. All
provisioned configurations are persisted somehow&amp;#x2F;somewhere so that you know which 
ports are already in use.&lt;/p&gt;
&lt;p&gt;So we don’t push the configuration to the web service instance f. e. by passing 
it as a program parameter (which is not easy or even possible if you have a big 
configuration structure) but we let the web service ask for the configuration 
when it is ready to do so.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How will the web service know where to ask for the configuration?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This can be achieved by setting an environment variable to the url the web service
instance will query. Keep in mind that the url also needs to be dynamic and unique
so that no other web service instance fetches the “wrong” configuration.&lt;/p&gt;
&lt;p&gt;Note: Don’t forget to add the &lt;code&gt;CPYENVVAR(*YES)&lt;/code&gt; parameter to the submit job command.&lt;/p&gt;
&lt;p&gt;So the code for loading the configuration in the web service may look something like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc loadConfig;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-pi *n pointer end-pi;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-pr getenv pointer extproc(&amp;#x27;getenv&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    name pointer value options(*string:*trim);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  end-pr;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s config pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s data varchar(1000);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s url varchar(1000);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s sqlString varchar(10000);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  // get the configuration url from the environment variable&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  url = %str(getenv(&amp;#x27;CONFIG_URL&amp;#x27;));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  if (url = *blank);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    message_escape(&amp;#x27;No config url provided.&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  endif;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  // get the configuration (in json format) from the previously retrieved url&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  sqlString = &amp;#x27;SELECT * FROM ( Values( SYSTOOLS.HTTPGETCLOB( ? ,&amp;#x27;&amp;#x27;&amp;#x27;&amp;#x27;) ) )&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  EXEC SQL PREPARE statement FROM :sqlString;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  EXEC SQL DECLARE c CURSOR FOR statement;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  EXEC SQL OPEN c USING :url;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  EXEC SQL FETCH FROM c INTO :data;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  if (sqlcode = 0);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    config = json_parseString(data);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  endif;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  return config;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  on-exit;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    EXEC SQL CLOSE c;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Note: This is only a prototype! Please add error handling where needed and keep
      in mind that this will start a Java VM for every job because of the usage
      of the SYSTOOLS HTTP SQL function.&lt;/p&gt;
&lt;p&gt;Happy provisioning!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2020/06/20/2020-06-20-ileastic-config/</guid>
            <title>Sharing Configuration in ILEastic</title>
            <link>http://example.com/2020/06/20/2020-06-20-ileastic-config/</link>
            <category>RPG</category>
            <pubDate>Sat, 20 Jun 2020 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;I stumbled upon a little problem I first had some trouble to wrap my head around.
My situation was the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I had an &lt;a href=&#34;https://github.com/sitemule/ILEastic&#34;&gt;ILEastic&lt;/a&gt; web service with multiple modules&lt;/li&gt;
&lt;li&gt;I wanted to share the configuration data with all modules&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So for this kind of internal sharing of data I had decided to use a global variable
I &lt;strong&gt;export&lt;/strong&gt;ed in one module and &lt;strong&gt;import&lt;/strong&gt;ed in the other module.&lt;/p&gt;
&lt;p&gt;So for my first module the code looked something like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;**FREE&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ctl-opt thread(*concurrent);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;/include &amp;#x27;ileastic/ileastic.rpgle&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;/include &amp;#x27;noxdb/jsonparser.rpgle&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-s userConfig pointer export;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;main();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;*inlr = *on;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc main;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-ds config likeds(il_config);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  userConfig = loadConfig();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  ...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;And the second module with the code for the web service endpoints looked something
like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;**FREE&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ctl-opt nomain thread(*CONCURRENT);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;/include &amp;#x27;ileastic/ileastic.rpgle&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-s userConfig pointer import;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;...&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Everything looked nice. The procedure &lt;code&gt;loadConfig&lt;/code&gt; did load the configuration and 
the configuration was assigned to the variable &lt;code&gt;userConfig&lt;/code&gt;. All seemed well but 
when the procedure in the second module accessed the imported variable the 
variable hold no value.&lt;/p&gt;
&lt;p&gt;Hmmm … &lt;/p&gt;
&lt;p&gt;After much reading in the IBM Knowledge Center I haven’t found anything which made
it work.&lt;/p&gt;
&lt;p&gt;And I noticed that I find most of my solutions on my way home from work or before
going to bed. And that was also the case here.&lt;/p&gt;
&lt;p&gt;I hadn’t taken into account that this was a multi-threaded application and with
&lt;code&gt;thread(*CONCURRENT)&lt;/code&gt; I have a new &lt;code&gt;userConfig&lt;/code&gt; variable for every thread I started.
So the value I previously set is not available in the thread I started. But I wanted
the configuration to be available in all threads so a little change to the code made
it happen.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-s userConfig pointer export static(*allthread);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;and&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-s userConfig pointer import static(*allthread);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now with the keywords &lt;code&gt;static(*ALLTHREAD)&lt;/code&gt; there is only one instance of this variable
for all threads.&lt;/p&gt;
&lt;p&gt;Next post will be about provisioning the web service with a dynamic configuration.&lt;/p&gt;
&lt;p&gt;Happy configuring!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2020/05/21/2020-05-21-ipkgbuilder/</guid>
            <title>I can package it all by myself</title>
            <link>http://example.com/2020/05/21/2020-05-21-ipkgbuilder/</link>
            <category>RPG</category>
            <pubDate>Thu, 21 May 2020 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;… and also provide those packages to others.&lt;/p&gt;
&lt;p&gt;But you probably know this expression much better as “I can read it all by myself”.
I have read those books from Dr. Seuss very often and with pleasure. Some were
real tongue twisters.&lt;/p&gt;
&lt;p&gt;But what I really like about the books is that they inspire you to do things yourself.
To not wait for others to do the work. And this is a great problem in the IBM i 
community as many are waiting for IBM to do the work. But when we look at other
platforms it is not the big companies who started the contributions to their community.
It is the small developer in his spare time. Getting done what he always wanted to
have or needed.&lt;/p&gt;
&lt;p&gt;And this is what the iPKG project is about. We need some easy way to build, package
and deploy our projects and iPKG can be a part of that.&lt;/p&gt;
&lt;p&gt;This post is about the packaging of software.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://bitbucket.org/m1hael/ipkgbuilder/&#34;&gt;&lt;em&gt;ipkgbuilder&lt;/em&gt; project&lt;/a&gt; (sister project 
of iPKG) creates RPM packages which are compatible with the iPKG client (but not with yum).&lt;/p&gt;
&lt;p&gt;It is written in Java and needs at least a Java 1.8 Runtime Environment (JRE). 
It can run either on the PC or on the IBM i server.&lt;/p&gt;
&lt;p&gt;The source will be available at the project site on Bitbucket.&lt;/p&gt;
&lt;p&gt;There is already a precompiled binary in the download section at the project site.&lt;/p&gt;
&lt;h2 id=&#34;Connection-Properties&#34;&gt;&lt;a href=&#34;#Connection-Properties&#34; class=&#34;headerlink&#34; title=&#34;Connection Properties&#34;&gt;&lt;/a&gt;Connection Properties&lt;/h2&gt;&lt;p&gt;The connection.properties file contains the following entries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;server&lt;/li&gt;
&lt;li&gt;user&lt;/li&gt;
&lt;li&gt;password&lt;/li&gt;
&lt;li&gt;buildlibrary&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are running the &lt;em&gt;ipkgbuilder&lt;/em&gt; on the IBM i server you can drop the entries &lt;em&gt;server, user&lt;/em&gt; and &lt;em&gt;password&lt;/em&gt; as it uses the current job for the connection.&lt;/p&gt;
&lt;h2 id=&#34;Spec-File&#34;&gt;&lt;a href=&#34;#Spec-File&#34; class=&#34;headerlink&#34; title=&#34;Spec File&#34;&gt;&lt;/a&gt;Spec File&lt;/h2&gt;&lt;p&gt;The spec file is a well formed XML file which contains everything about the content of the RPM file.&lt;/p&gt;
&lt;p&gt;The structure of the XML is pretty flat. It has a top element &lt;code&gt;package&lt;/code&gt; with a couple of sub elements.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;name : package name&lt;/li&gt;
&lt;li&gt;version : package version&lt;/li&gt;
&lt;li&gt;buildVersion : build version&lt;/li&gt;
&lt;li&gt;group : category of the package, see &lt;a href=&#34;https://fedoraproject.org/wiki/RPMGroups&#34;&gt;list of categories&amp;#x2F;groups&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;summary : summary of the package content (one liner)&lt;/li&gt;
&lt;li&gt;description : more detailed description of the content&lt;/li&gt;
&lt;li&gt;license : license of the software&lt;/li&gt;
&lt;li&gt;url : URL of the software (like project website)&lt;/li&gt;
&lt;li&gt;vendor : vendor or author of the software&lt;/li&gt;
&lt;li&gt;packager : name of the packager (which may be the same as vendor but don’t have to)&lt;/li&gt;
&lt;li&gt;osVersion : Version of the IBM i operating system&lt;/li&gt;
&lt;li&gt;requirement : dependencies of the package&lt;/li&gt;
&lt;li&gt;provides : specifies what the package provides besides itself (see &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/wiki/Virtual_Packages/&#34;&gt;virtual packages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;dataPackage : mark package as a data package&lt;/li&gt;
&lt;li&gt;directoryPrefix : The directory prefix specified the part of the file path which can be replaced on installation, see &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/wiki/Client#markdown-header-location&#34;&gt;relocation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;exportedModule : see &lt;a href=&#34;TODO&#34;&gt;iPKG documentation about IPKG binding directory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;exportedServiceProgram : same as exportedModule&lt;/li&gt;
&lt;li&gt;file : absolute file path of each packaged file or object&lt;/li&gt;
&lt;li&gt;purgePrefix : remove beginning part of the file path&lt;/li&gt;
&lt;li&gt;preInstallScript : QCMDEXC script, see &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/wiki/Package#markdown-header-scripts&#34;&gt;iPKG documentation about scripts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;postInstallScript : QCMDEXC script, see &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/wiki/Package#markdown-header-scripts&#34;&gt;iPKG documentation about scripts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;preUninstallScript : QCMDEXC script, see &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/wiki/Package#markdown-header-scripts&#34;&gt;iPKG documentation about scripts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;postUninstallScript : QCMDEXC script, see &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/wiki/Package#markdown-header-scripts&#34;&gt;iPKG documentation about scripts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At &lt;a href=&#34;https://bitbucket.org/m1hael/specfiles/&#34;&gt;my spec files repository&lt;/a&gt; you can see the 
spec files used for the repository at &lt;a href=&#34;https://repo.rpgnextgen.com/&#34;&gt;RPG Next Gen&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Example spec file of the &lt;em&gt;stream&lt;/em&gt; project:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; ?&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;package&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;name&amp;gt;stream&amp;lt;/name&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;version&amp;gt;1.0.0&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;buildVersion&amp;gt;1&amp;lt;/buildVersion&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;group&amp;gt;Development/Library&amp;lt;/group&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;summary&amp;gt;Streaming API for ILE&amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;description&amp;gt;A stream consist of an emitter which provides the data for the stream. The data can be piped through pipes and may end in a sink.&amp;lt;/description&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;license&amp;gt;MIT&amp;lt;/license&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;url&amp;gt;https://bitbucket.org/m1hael/stream/&amp;lt;/url&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;vendor&amp;gt;Mihael Schmidt&amp;lt;/vendor&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;packager&amp;gt;Mihael Schmidt&amp;lt;/packager&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;osVersion&amp;gt;7.3&amp;lt;/osVersion&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;requirement&amp;gt;arraylist &amp;amp;gt;= 2.0.0&amp;lt;/requirement&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;requirement&amp;gt;arraylist &amp;amp;lt; 3.0.0&amp;lt;/requirement&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;requirement&amp;gt;message &amp;amp;gt;= 1.1.0&amp;lt;/requirement&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;requirement&amp;gt;message &amp;amp;lt; 2.0.0&amp;lt;/requirement&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;dataPackage&amp;gt;false&amp;lt;/dataPackage&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;directoryPrefix&amp;gt;/QSYS.LIB/MIHAEL.LIB&amp;lt;/directoryPrefix&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;exportedServiceProgram&amp;gt;/QSYS.LIB/MIHAEL.LIB/STREAM.SRVPGM&amp;lt;/exportedServiceProgram&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;file&amp;gt;/QSYS.LIB/MIHAEL.LIB/STREAM.SRVPGM&amp;lt;/file&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;	&amp;lt;purgePrefix&amp;gt;&amp;lt;/purgePrefix&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/package&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h3 id=&#34;Package-Version&#34;&gt;&lt;a href=&#34;#Package-Version&#34; class=&#34;headerlink&#34; title=&#34;Package Version&#34;&gt;&lt;/a&gt;Package Version&lt;/h3&gt;&lt;p&gt;The &lt;em&gt;version&lt;/em&gt; is best specified in the &lt;a href=&#34;https://semver.org/&#34;&gt;semantic versioning scheme&lt;/a&gt;, 
like &lt;code&gt;x.y.z&lt;/code&gt; where &lt;em&gt;x&lt;/em&gt; is the major version number, &lt;em&gt;y&lt;/em&gt; is the minor version number and 
&lt;em&gt;z&lt;/em&gt; is the revision number. The version can also be specified as &lt;code&gt;x.y&lt;/code&gt; or just &lt;code&gt;x&lt;/code&gt;. Only 
numbers are allowed in the version number. No other characters and no version qualifiers.&lt;/p&gt;
&lt;h3 id=&#34;Build-Version&#34;&gt;&lt;a href=&#34;#Build-Version&#34; class=&#34;headerlink&#34; title=&#34;Build Version&#34;&gt;&lt;/a&gt;Build Version&lt;/h3&gt;&lt;p&gt;The &lt;em&gt;buildVersion&lt;/em&gt; is the version of the package used by the packager to identify 
differences in packages to previous versions of the package. The original content 
of the package may be the same but the packager may have patched it and thus the 
package needs to be marked different from the previous package. The build version 
consist of a single number.&lt;/p&gt;
&lt;h3 id=&#34;OS-Version&#34;&gt;&lt;a href=&#34;#OS-Version&#34; class=&#34;headerlink&#34; title=&#34;OS Version&#34;&gt;&lt;/a&gt;OS Version&lt;/h3&gt;&lt;p&gt;The &lt;em&gt;osVersion&lt;/em&gt; is also specified using the semantic versioning scheme, f. e. &lt;code&gt;7.3&lt;/code&gt; .&lt;/p&gt;
&lt;h3 id=&#34;Requirements&#34;&gt;&lt;a href=&#34;#Requirements&#34; class=&#34;headerlink&#34; title=&#34;Requirements&#34;&gt;&lt;/a&gt;Requirements&lt;/h3&gt;&lt;p&gt;Each requirement is specified in a new &lt;code&gt;requirement&lt;/code&gt; element. See the 
&lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/wiki/Requirements&#34;&gt;iPKG documentation&lt;/a&gt; what can be 
specified as a requirement and how.&lt;/p&gt;
&lt;h3 id=&#34;Data-Package&#34;&gt;&lt;a href=&#34;#Data-Package&#34; class=&#34;headerlink&#34; title=&#34;Data Package&#34;&gt;&lt;/a&gt;Data Package&lt;/h3&gt;&lt;p&gt;A data package is treated as the other packages. The only difference is the handling 
of the location where the content is placed. The objects will be restored in the data 
library specified on the command, see parameter &lt;code&gt;DTALIB&lt;/code&gt;. Valid values are &lt;code&gt;true&lt;/code&gt; and
&lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;Build&#34;&gt;&lt;a href=&#34;#Build&#34; class=&#34;headerlink&#34; title=&#34;Build&#34;&gt;&lt;/a&gt;Build&lt;/h2&gt;&lt;p&gt;There is a ready to use jar file in the download section at the project site. 
The program needs the following parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;connection.properties&lt;/em&gt; : Properties files which contains the connection settings and the build library&lt;/li&gt;
&lt;li&gt;&lt;em&gt;spec file&lt;/em&gt; : XML file which contains the definition of the RPM file&lt;/li&gt;
&lt;li&gt;&lt;em&gt;output directory&lt;/em&gt; : Directory for the RPM file&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;java -jar ipkgbuilder.jar connection.properties my_project.spec packages_directory
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: The name of the RPM file is determined by the attributes in the spec file, 
like name, version, etc… .&lt;/p&gt;
&lt;h3 id=&#34;QSYS-LIB&#34;&gt;&lt;a href=&#34;#QSYS-LIB&#34; class=&#34;headerlink&#34; title=&#34;QSYS.LIB&#34;&gt;&lt;/a&gt;QSYS.LIB&lt;/h3&gt;&lt;p&gt;Objects from the QSYS.LIB file system are packed into a save file in the build 
library. The save file is the payload of the RPM package.&lt;/p&gt;
&lt;h3 id=&#34;IFS&#34;&gt;&lt;a href=&#34;#IFS&#34; class=&#34;headerlink&#34; title=&#34;IFS&#34;&gt;&lt;/a&gt;IFS&lt;/h3&gt;&lt;p&gt;All other stream files are packed into a zip file in the &lt;code&gt;/tmp&lt;/code&gt; folder. The zip 
file is the payload of the RPM package.&lt;/p&gt;
&lt;h3 id=&#34;Check-RPM-Entries&#34;&gt;&lt;a href=&#34;#Check-RPM-Entries&#34; class=&#34;headerlink&#34; title=&#34;Check RPM Entries&#34;&gt;&lt;/a&gt;Check RPM Entries&lt;/h3&gt;&lt;p&gt;You can check the entries of the RPM file with the embedded 
&lt;a href=&#34;https://github.com/m1h43l/redline&#34;&gt;redline&lt;/a&gt; library.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;java -cp ipkgbuilder.jar org.redline_rpm.Scanner my_project-1.0.0-ppc-ibmi-7.3.rpm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Scanner program outputs all RPM header entries (and chokes on the payload).&lt;/p&gt;
&lt;h2 id=&#34;Repository&#34;&gt;&lt;a href=&#34;#Repository&#34; class=&#34;headerlink&#34; title=&#34;Repository&#34;&gt;&lt;/a&gt;Repository&lt;/h2&gt;&lt;p&gt;One of the reasons for choosing the RPM format was the existing tools available
on various platforms to generate and process RPM packages.&lt;/p&gt;
&lt;p&gt;One of those tools is &lt;code&gt;createrepo&lt;/code&gt; which takes a set of packages and generates 
some XML files about them. Those XML are processed by the iPKG client to get 
some information about the repository and the packages hosted in that repository.&lt;/p&gt;
&lt;p&gt;Note: &lt;code&gt;createrepo&lt;/code&gt; is also available on IBM i via yum.&lt;/p&gt;
&lt;h2 id=&#34;Wrap-it-all-together&#34;&gt;&lt;a href=&#34;#Wrap-it-all-together&#34; class=&#34;headerlink&#34; title=&#34;Wrap it all together&#34;&gt;&lt;/a&gt;Wrap it all together&lt;/h2&gt;&lt;p&gt;A build script for a repository might look like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;#!/bin/bash&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;rm -rf repository/repodata&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;SPECFILES=$(ls specfiles)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;for SPECFILE in $SPECFILES&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;do&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    echo &amp;quot;Building from spec file $SPECFILE ...&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    java -jar ipkgbuilder.jar connection.properties specfiles/$SPECFILE repository/&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;done&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;createrepo --no-database --content ibmi --distro rpgnextgen -p ./repository/ -s md5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;# ipkg doesn&amp;#x27;t process gzip files yet =&amp;gt; need to unzip&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;gzip -k -d ./repository/repodata/*.gz&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;# provide a convenient zip file for offline installation&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;rm -f rpgnextgen-repository.*&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;zip -r rpgnextgen-repository.zip repository&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;md5sum -b rpgnextgen-repository.zip &amp;gt; rpgnextgen-repository.md5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;# upload all to hosting platform&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ssh rpgnextgen.com@ssh.rpgnextgen.com &amp;quot;rm -rf /www/repo/repository/*&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;scp -r repository/* rpgnextgen.com@ssh.rpgnextgen.com:/www/repo/repository/&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;scp -r repository/repodata/*primary.xml rpgnextgen.com@ssh.rpgnextgen.com:/www/repo/repository/repodata/primary.xml&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;scp rpgnextgen-repository.* rpgnextgen.com@ssh.rpgnextgen.com:/www/repo/repository/&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;# purge cache on hosting platform&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ssh rpgnextgen.com@ssh.rpgnextgen.com &amp;quot;cache-purge https://repo.rpgnextgen.com&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;h2 id=&#34;What’s-next&#34;&gt;&lt;a href=&#34;#What’s-next&#34; class=&#34;headerlink&#34; title=&#34;What’s next?&#34;&gt;&lt;/a&gt;What’s next?&lt;/h2&gt;&lt;p&gt;Now we can easily script the build of RPM packages. The next level of package 
creation automation would be the integration into a build server like 
&lt;a href=&#34;https://jenkins.io/&#34;&gt;Jenkins&lt;/a&gt;. Jenkins uses pipelines to define the build script. 
So having a pipeline command for creating an RPM package would be great.&lt;/p&gt;
&lt;p&gt;What do you think? Please send me your feedback!&lt;/p&gt;
&lt;p&gt;Happy packaging!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2020/05/08/2020-05-08-streaming-api/</guid>
            <title>Streaming API for ILE</title>
            <link>http://example.com/2020/05/08/2020-05-08-streaming-api/</link>
            <category>RPG</category>
            <pubDate>Fri, 08 May 2020 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;For those of you who cannot associate anything with streams in programming:&lt;/p&gt;
&lt;p&gt;A stream consists of an emitter (provider) of data and a sequence of one or 
more consumers of data whereas the consumer may pass the data further to the 
next consumer constructing a streaming data pipeline.&lt;/p&gt;
&lt;h2 id=&#34;Emitter&#34;&gt;&lt;a href=&#34;#Emitter&#34; class=&#34;headerlink&#34; title=&#34;Emitter&#34;&gt;&lt;/a&gt;Emitter&lt;/h2&gt;&lt;p&gt;The emitter can produce a fixed or infinite number of data blocks. A stream 
is not restricted to one type of data. The emitter produces events which just 
hold a pointer to the data. Behind the pointer can lie any kind of data.&lt;/p&gt;
&lt;h2 id=&#34;Consumer&#34;&gt;&lt;a href=&#34;#Consumer&#34; class=&#34;headerlink&#34; title=&#34;Consumer&#34;&gt;&lt;/a&gt;Consumer&lt;/h2&gt;&lt;p&gt;Consumer of data can be categorized in pipes and sinks.&lt;/p&gt;
&lt;p&gt;A pipe has multiple action choices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;process the data and push it to the next consumer&lt;/li&gt;
&lt;li&gt;change the data but keep the type and push it to the next consumer&lt;/li&gt;
&lt;li&gt;transform the data to a different type and push it to the next consumer&lt;/li&gt;
&lt;li&gt;split the data into multiple chunks of data&lt;/li&gt;
&lt;li&gt;drop the data and do not push it to the next consumer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A sink sits at the end of the pipeline and “drains” all events. It is the 
end of the process. After the sink processed the data the emitter emits 
the next event.&lt;/p&gt;
&lt;p&gt;Consumer can provide various kind of functionality:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;filter&lt;/li&gt;
&lt;li&gt;aggregate&lt;/li&gt;
&lt;li&gt;sum&lt;/li&gt;
&lt;li&gt;group&lt;/li&gt;
&lt;li&gt;distinct&lt;/li&gt;
&lt;li&gt;map&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;Example&#34;&gt;&lt;a href=&#34;#Example&#34; class=&#34;headerlink&#34; title=&#34;Example&#34;&gt;&lt;/a&gt;Example&lt;/h2&gt;&lt;p&gt;Emitter: Reads a file and pushes the data block by block to the next consumer
Pipe: Calls the Qc3CalculateHash System API for every block of data
Sink: Gets the calculated hash after the last block of data from the previous pipe and stores the hash&lt;/p&gt;
&lt;p&gt;The code may look like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;// setup the stream&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;stream = stream_create(emitter_file(&amp;#x27;data.txt&amp;#x27;)); // emits data from the file in blocks&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;stream_pipe(stream : pipe_md5());                 // calculates the md5 for the data block&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;stream_sink(stream : %paddr(writeMd5));           // local procedure storing the data&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// start the stream , let the data flow&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;stream_flow(stream);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// cleanup the mess =)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;stream_dispose(stream);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;h2 id=&#34;API-Documentation&#34;&gt;&lt;a href=&#34;#API-Documentation&#34; class=&#34;headerlink&#34; title=&#34;API Documentation&#34;&gt;&lt;/a&gt;API Documentation&lt;/h2&gt;&lt;p&gt;The API of this project is documented using &lt;a href=&#34;https://bitbucket.org/m1hael/iledocs-page-builder&#34;&gt;ILEDocs&lt;/a&gt; 
and can be viewed at the &lt;a href=&#34;https://iledocs.rpgnextgen.com/&#34;&gt;ILEDocs&lt;/a&gt; installation at 
&lt;a href=&#34;https://rpgnextgen.com/&#34;&gt;RPG Next Gen&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;Installation&#34;&gt;&lt;a href=&#34;#Installation&#34; class=&#34;headerlink&#34; title=&#34;Installation&#34;&gt;&lt;/a&gt;Installation&lt;/h2&gt;&lt;p&gt;The project can either be build by using the &lt;code&gt;make&lt;/code&gt; tool or installed with the
&lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/&#34;&gt;iPKG&lt;/a&gt; client. It can be as simple as&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ipkg install stream
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The emitter will be packaged separately but can be as easily installed:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ipkg install &amp;#39;stream-emitter&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No need to build from source.&lt;/p&gt;
&lt;p&gt;Note: Use the LOC parameter on the &lt;code&gt;ipkg&lt;/code&gt; command to specify where the object will be installed to.&lt;/p&gt;
&lt;p&gt;The copybooks (prototypes, constants and templates) are packaged into separate packages.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;stream-devel&lt;/li&gt;
&lt;li&gt;stream-emitter-devel&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note: When installing the &lt;em&gt;devel&lt;/em&gt; packages the &lt;code&gt;LOC&lt;/code&gt; parameter accepts an IFS path for 
      the copybook stream files.&lt;/p&gt;
&lt;p&gt;Pipes will also be packaged in a separate package. But the project doesn’t contain
any pipes yet.&lt;/p&gt;
&lt;h2 id=&#34;Ideas&#34;&gt;&lt;a href=&#34;#Ideas&#34; class=&#34;headerlink&#34; title=&#34;Ideas&#34;&gt;&lt;/a&gt;Ideas&lt;/h2&gt;&lt;p&gt;What can be further expected from the project? More pipes and emitters will
definitely follow like data queue emitters or random number emitters.&lt;/p&gt;
&lt;p&gt;The big goal is to be able to not just sequentially process the data but to
be able to spawn new threads from a consumer and process the data in parallel.&lt;/p&gt;
&lt;h2 id=&#34;Feedback&#34;&gt;&lt;a href=&#34;#Feedback&#34; class=&#34;headerlink&#34; title=&#34;Feedback&#34;&gt;&lt;/a&gt;Feedback&lt;/h2&gt;&lt;p&gt;I am happy for every feedback and contribution.&lt;/p&gt;
&lt;p&gt;Happy streaming!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2020/04/25/2020-04-25-ipkg-beta2/</guid>
            <title>iPKG beta 2</title>
            <link>http://example.com/2020/04/25/2020-04-25-ipkg-beta2/</link>
            <category>RPG</category>
            <pubDate>Sat, 25 Apr 2020 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;Hi folks!&lt;/p&gt;
&lt;p&gt;The next beta is out with some new features.&lt;/p&gt;
&lt;p&gt;First a simple but important one: &lt;code&gt;VERSION&lt;/code&gt; action. Now you can see which iPKG client version you got. 
Currently the version is stored in the user defined attribute of the object. But this can only store 
10 characters and only one value. There is an RFE &lt;a href=&#34;http://www.ibm.com/developerworks/rfe/execute?use_case=viewRfe&amp;CR_ID=100681&#34;&gt;Add more user defined attributes&amp;#x2F;object information 
to an object&lt;/a&gt; out to 
extend that. Please vote for it so we can store more metadata directly on the object itself.&lt;/p&gt;
&lt;p&gt;Back to business: Dependencies can now also be set to an installed product or PTF (thanks to Holger 
Scherer and Kevin Adler for their help). For more information see the wiki page &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/wiki/Requirements&#34;&gt;Requirements&lt;/a&gt;. 
Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;5770DG1_SI71714 = 7.4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There has also been more work on the opposite side where the package can define what it provides. 
By default a package provides itself. But now it is also possible to define more capabilities of 
a package on which a dependency can be expressed. F. e. a package needs a HTTP client as a dependency. 
It doesn’t matter which HTTP client as long as there is at least one installed. So it defines a 
requirement to &lt;code&gt;httpclient&lt;/code&gt;. Welcome to the land of &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/wiki/Virtual_Packages/&#34;&gt;virtual packages&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And as I mentioned HTTP … you can now specify a repository with an HTTP or HTTPS protocol which 
means you don’t have to manually download and “install” the repository but can use the existing 
one on the web as is.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ipkg addrepo &amp;quot;RPGNextGen https://repo.rpngextgen.com/repository&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There has also been some bug fixes and some database changes so you need to remove the old iPKG 
tables and let the client install the new one.&lt;/p&gt;
&lt;p&gt;I added the Java RPM library &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/downloads/redline.jar&#34;&gt;redline&lt;/a&gt; 
to the download section of the iPKG project. You can create packages programmatically with it. I 
also uploaded an &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/downloads/HelloRubyPackager.java&#34;&gt;example&lt;/a&gt; on 
how to do that.&lt;/p&gt;
&lt;p&gt;I hope more people will give this project &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/downloads/&#34;&gt;a try&lt;/a&gt;. 
ANY feedback is welcome.&lt;/p&gt;
&lt;p&gt;BTW: Is there any interest into automatic building of RPM packages like this?&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.rpgnextgen.com/images/ipkg-buildall.png&#34; alt=&#34;build all packages with redline&#34;&gt;&lt;/p&gt;
&lt;p&gt;This could be coupled with the source code checkout and build of a project in a Jenkins pipeline 
which makes a wonderful CI&amp;#x2F;CD process &amp;#x3D;) .&lt;/p&gt;
&lt;p&gt;Happy building and testing!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2020/04/16/2020-04-16-ipkg-feedback/</guid>
            <title>iPKG ... package management for ILE in beta phase ... and now what?</title>
            <link>http://example.com/2020/04/16/2020-04-16-ipkg-feedback/</link>
            <category>RPG</category>
            <pubDate>Thu, 16 Apr 2020 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;Hi folks!&lt;/p&gt;
&lt;p&gt;Happy Easter!&lt;/p&gt;
&lt;p&gt;I am in the midst of developing an open source package management system for IBM i. Now many people may say: “We already have yum for managing packages on IBM i. I should get up-to-date.” Yes … and no.&lt;/p&gt;
&lt;p&gt;Yes: yum manages packages&lt;br&gt;No: It does not manage packages for the whole IBM i system, but only those meant for PASE.  &lt;/p&gt;
&lt;p&gt;&lt;em&gt;Where are the packages for ILE?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There is a lot of good open source software for ILE (which is a great environment by the way &amp;#x3D;) ). There was a discussion a decade ago about an app store for IBM i but that faded away as fast as it came and without any result at all (and with the demise of developerworks probably any traces have been erased). &lt;/p&gt;
&lt;p&gt;So I set up a project and am now at a stage where the community can jump in and help. I got a client for managing packages. The client is a native ILE program (RPG) with a command object, see save file at the download section &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/downloads/&#34;&gt;https://bitbucket.org/m1hael/ipkg/downloads/&lt;/a&gt; . It is available for IBM i 7.3 (and of course compatible with 7.4). &lt;/p&gt;
&lt;p&gt;I packaged some of my own projects. These packages are available at &lt;a href=&#34;https://repo.rpgnextgen.com/&#34;&gt;https://repo.rpgnextgen.com&lt;/a&gt; .&lt;/p&gt;
&lt;p&gt;The client is at a late alpha&amp;#x2F;early beta stage I would say.&lt;/p&gt;
&lt;p&gt;And this is the part where you can jump in: I need some testers and some feedback.&lt;/p&gt;
&lt;p&gt;The project is hosted at &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/&#34;&gt;https://bitbucket.org/m1hael/ipkg/&lt;/a&gt; . To get into the project&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;Read the README.md&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Read the wiki articles&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Download and try out the client&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Currently the client can only install files on the local file system. No installation vie http&amp;#x2F;https. But you can download the whole RPG Next Gen repository (just 1 MB) and put it in the IFS.&lt;/p&gt;
&lt;p&gt;How to give feedback?&lt;/p&gt;
&lt;p&gt;First things first: ANY feedback is welcome!&lt;/p&gt;
&lt;p&gt;You can &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;write your feedback in the Ryver forum ( &lt;a href=&#34;https://ibmioss.ryver.com/#posts/2360640&#34;&gt;https://ibmioss.ryver.com/#posts/2360640&lt;/a&gt; )&lt;/li&gt;
&lt;li&gt;create a ticket at &lt;a href=&#34;https://bitbucket.org/m1hael/ipkg/issues/&#34;&gt;https://bitbucket.org/m1hael/ipkg/issues/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;mail me directly : &lt;a href=&#34;mailto:mihael@rpgnextgen.com&#34;&gt;mihael@rpgnextgen.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I don’t care which way you use. Just do it! &lt;/p&gt;
&lt;p&gt;I will collect everything as tickets in Bitbucket because it will be easier to work with the feedback from there on.&lt;/p&gt;
&lt;p&gt;Many thanks to those investing their precious time (which is one of the most precious resources you have). If you have any questions just ask!&lt;/p&gt;
&lt;p&gt;Happy testing!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2020/04/15/2020-04-15-unixtimestamp/</guid>
            <title>Creating a Unix Timestamp</title>
            <link>http://example.com/2020/04/15/2020-04-15-unixtimestamp/</link>
            <category>RPG</category>
            <pubDate>Wed, 15 Apr 2020 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;As most people know the Unix timestamp is an integer value which represents
the seconds from 1970-01-01.&lt;/p&gt;
&lt;p&gt;There is currently no built-in function for it but you can easily calculate 
it by yourself.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;**FREE&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ctl-opt dftactgrp(*no) actgrp(*caller);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-pr sys_getUtcOffset extproc(&amp;#x27;CEEUTCO&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  offsetHours int(10);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  offsetMinutes int(10);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  offsetSeconds float(8);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  feedback char(12) options(*omit);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-pr;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-c UNIX_EPOCH_START z&amp;#x27;1970-01-01-00.00.00.000000&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;main();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;*inlr = *on;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc main;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s uxts int(10);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s now timestamp;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s offsetHours int(10);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s offsetMinutes int(10);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s offsetSeconds float(8);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  now = z&amp;#x27;2020-04-15-00.00.00.000&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  sys_getUtcOffset(offsetHours : offsetMinutes : offsetSeconds : *omit);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  uxts = %diff(now : UNIX_EPOCH_START : *SECONDS) - offsetSeconds;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  // Output : 1586908800&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dsply %char(uxts);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt; ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2020/03/07/2020-03-08-ipkg-global-local/</guid>
            <title>iPKG - Global = Local</title>
            <link>http://example.com/2020/03/07/2020-03-08-ipkg-global-local/</link>
            <category>iPKG</category>
            <pubDate>Sat, 07 Mar 2020 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;In most contexts this is not true : &lt;code&gt;Global = Local&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;But in the case of iPKG there is no difference between a global , for all users, 
installation of packages or a local installation (scoped to a single user) from 
the perspective of the package manager.&lt;/p&gt;
&lt;p&gt;Objects get restored in a library and stream files in the IFS. It doesn’t matter 
to the package manager if that library is in some library list or not. So you 
can easily have one installation in a dedicated library for all users and have 
another installation in a different library with packages from different 
repositories (or the same) , f. e. for development purposes. &lt;/p&gt;
&lt;br/&gt;

&lt;img src=&#34;http://rpgnextgen.com/images/ipkg-install-location-default-scaled.png&#34; /&gt;

&lt;br/&gt;

&lt;p&gt;The client lets you decide where to put the packages, see parameter iPKG library 
or &lt;code&gt;IPKGLIB&lt;/code&gt;. This is also the place where all the package management data for
this installation will be stored (all objects starting with &lt;code&gt;IPKG&lt;/code&gt;). But you can
also split this and have the objects located to another library, see parameter
location or &lt;code&gt;LOC&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I know that some sites like to separate data objects from program objects and 
have their files, tables, views and indexes in a different library. This is 
also supported by iPKG.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2020/02/15/2020-02-15-template-rest/</guid>
            <title>Node.js Template Project</title>
            <link>http://example.com/2020/02/15/2020-02-15-template-rest/</link>
            <category>node.js</category>
            <pubDate>Sat, 15 Feb 2020 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;During my journey through the Node.js universe I have missed a good template for what I wanted to do. I searched for a simple template for a REST service on which I can build on.&lt;/p&gt;
&lt;p&gt;You can start with this by following the next couple of steps and are ready to go&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;git clone &lt;a href=&#34;https://m1hael@bitbucket.org/m1hael/template-rest/&#34;&gt;https:&amp;#x2F;&amp;#x2F;m1hael@bitbucket.org/m1hael/template-rest.git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;npm install&lt;/li&gt;
&lt;li&gt;npm run build&lt;/li&gt;
&lt;li&gt;npm start&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This template also works out-of-the-box on IBM i.&lt;/p&gt;
&lt;p&gt;I like the &lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/rested/&#34;&gt;RESTED&lt;/a&gt; client  for Firefox to test my services and also store the request for later reuse. The add on is also available for &lt;a href=&#34;https://chrome.google.com/webstore/detail/rested/eelcnbccaccipfolokglfhhmapdchbfg&#34;&gt;Chrome&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you have started the service you can test it by making the following HTTP request:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET http://localhost:3000/api/date?format=dd.MM.yyyy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The template also provides a Swagger interface for the REST service at &lt;a href=&#34;http://localhost:3000/swagger&#34;&gt;http://localhost:3000/swagger&lt;/a&gt; .&lt;/p&gt;
&lt;p&gt;The directory structure of the project is pretty straight:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;config - configuration files&lt;/li&gt;
&lt;li&gt;logs - logging directory (if not configured)&lt;/li&gt;
&lt;li&gt;src - source code&lt;ul&gt;
&lt;li&gt;controller - REST endpoints&lt;/li&gt;
&lt;li&gt;docs - documentation&lt;/li&gt;
&lt;li&gt;middleware&lt;/li&gt;
&lt;li&gt;models - data models&lt;/li&gt;
&lt;li&gt;startup - scripts which are executed during service startup like configuring logging&lt;/li&gt;
&lt;li&gt;services&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Application entry point is the &lt;code&gt;src/server.ts&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;The template uses the &lt;a href=&#34;http://lorenwest.github.io/node-config/&#34;&gt;config&lt;/a&gt; package for configuration. For most things a mapping to an environment variable is configured to keep it flexible.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://github.com/winstonjs/winston#readme&#34;&gt;winston&lt;/a&gt; and &lt;a href=&#34;https://github.com/expressjs/morgan#readme&#34;&gt;morgan&lt;/a&gt; package has been used for logging. morgan has been used for creating an access log and winston for everything else. winston is configured to log in the logstash compatible format. The log entries also contains the application name as an additional field (see &lt;code&gt;middleware/logapp.ts&lt;/code&gt;) so that you can easily filter for the application when using something like the &lt;a href=&#34;https://www.elastic.co/de/what-is/elk-stack&#34;&gt;ELK&lt;/a&gt; stack.&lt;/p&gt;
&lt;p&gt;The Startup class also accepts an object in the constructor. This can be used to pass in or get out values to&amp;#x2F;from the startup scripts. Each startup script is an implementation of the Runnable interface.&lt;/p&gt;
&lt;p&gt;During development I like to have at least two terminals open: one for compiling the application with “npm run build:watch” and another for running the application with “npm run watch” (which uses the &lt;a href=&#34;http://nodemon.io/&#34;&gt;nodemon&lt;/a&gt; package). This ensures a short code-compile-run-test development cycle.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2019/03/15/2019-03-15-libxlsxwriter/</guid>
            <title>libxlsxwriter - Date/Time and Image Support</title>
            <link>http://example.com/2019/03/15/2019-03-15-libxlsxwriter/</link>
            <category>RPG</category>
            <category>libxlsxwriter</category>
            <pubDate>Fri, 15 Mar 2019 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;libxlsxwriter is a great library for creating documents in the 
&lt;a href=&#34;http://docs.oasis-open.org/office/v1.2/cs01/OpenDocument-v1.2-cs01.html&#34;&gt;Open Document spreadsheet format&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Even so its code is very strict (and good) ANSI C code some things does not 
work out-of-the-box for integration in RPG and must be tweaked a little bit.&lt;/p&gt;
&lt;p&gt;This was also the case for image and date&amp;#x2F;time support which has now been 
added to the forked repository at &lt;a href=&#34;https://bitbucket.org/m1hael/libxlsxwriter&#34;&gt;Bitbucket.org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Image support is pretty simple. Currently only support for PNGs has been
successfully tested and it looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;worksheet_insert_image(worksheet : 3 : 2 : &amp;#39;logo.png&amp;#39;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But you can also add some options like scaling or positional offsets.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-ds imageOptions likeds(lxw_image_options) inz;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;imageOptions.xScale = 0.5;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;imageOptions.yScale = 0.5;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;worksheet_insert_image_opt(worksheet : 3 : 2 : &amp;#x27;/home/mihael/logo512.png&amp;#x27; : imageOptions);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;To support date&amp;#x2F;time in RPG we need an additional data structure which is not
part of the original libxlsxwriter project, &lt;code&gt;lxw_datetime&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-ds datetime likeds(lxw_datetime) inz;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-s dateFormat pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dateFormat = workbook_add_format(workbook);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;format_set_num_format(dateFormat : &amp;#x27;dd.mm.yyyy&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;datetime = lxw_datetime_create(%date());&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;worksheet_write_datetime(worksheet : x : 2 : datetime : dateFormat);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;And when it is finished it looks like this:&lt;/p&gt;
&lt;img src=&#34;https://www.rpgnextgen.com/images/libxlsxwriter-image-datetime.png&#34; /&gt;

&lt;p&gt;You can find the code for the above document &lt;a href=&#34;https://rpgnextgen.com/downloads/libxlsx02.rpgle&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you compile the project you should state your CCSID on the make command like&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;make -f Makefile.ibm TARGET_CCSID=273 env compile bind
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For easier installation a save file and an archive file with the copybooks are
available in the download section.&lt;/p&gt;
&lt;p&gt;Happy spreadsheeting!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2019/02/27/2019-02-27-open-document-escaped/</guid>
            <title>Write Unicode Characters with libxlsxwriter</title>
            <link>http://example.com/2019/02/27/2019-02-27-open-document-escaped/</link>
            <category>RPG</category>
            <category>libxlsxwriter</category>
            <pubDate>Wed, 27 Feb 2019 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;Hi,&lt;/p&gt;
&lt;p&gt;the project &lt;a href=&#34;https://github.com/jmcnamara/libxlsxwriter&#34;&gt;libxlsxwriter&lt;/a&gt; lets your 
programmatically create spreadsheets in the Open Document format which is also used 
by OpenOffice, Microsoft Office, … .&lt;/p&gt;
&lt;p&gt;Sometimes you need to add a special sign to some cell, f. e. the Euro currency 
symbol. If your native codepage does not support it you can still code it by 
escaping it. Characters can be escaped with the syntax _xHHHH_ where HHHH is 
the hex value of the unicode character, _x20AC_ for the euro sign.&lt;/p&gt;
&lt;p&gt;This can again be escaped with escaping the first underscore with _x005F_. 
_x005F_x20AC_ will literally write the string and not the euro sign.&lt;/p&gt;
&lt;p&gt;See &lt;a href=&#34;http://opendocument.xml.org/&#34;&gt;Ecma Office Open XML Part 1&lt;/a&gt; - Fundamentals and 
Markup Language Reference , 22.4.2.4 bstr (Basic String) at page 3736.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-s moneyFormat pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;moneyFormat = workbook_add_format(workbook);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;format_set_num_format(moneyFormat : &amp;#x27;#,##0.00 &amp;quot;_x20AC_&amp;quot;&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;worksheet_write_number(worksheet : 3 : 1 : 1499.99 : moneyFormat);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Happy escaping!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2018/07/27/2018-07-27-arraylist-bugfix/</guid>
            <title>Arraylist - Bugfix</title>
            <link>http://example.com/2018/07/27/2018-07-27-arraylist-bugfix/</link>
            <category>RPG</category>
            <category>arraylist</category>
            <pubDate>Fri, 27 Jul 2018 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;A little memory leak has crept into the code.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;arraylist_dispose()&lt;/code&gt; method did not deallocate all the memory which has
been previously allocated. This has been fixed now in the current version.&lt;/p&gt;
&lt;p&gt;Every user of the JSON service program also needs to update the arraylist
service program as it is used as a backend for the JSON array implementation.&lt;/p&gt;
&lt;p&gt;You can find the new version on the &lt;a href=&#34;https://bitbucket.org/m1hael/arraylist&#34;&gt;project page&lt;/a&gt; at Bitbucket.&lt;/p&gt;
&lt;p&gt;Happy updating!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2018/06/21/2018-06-21-bluedroplet/</guid>
            <title>BlueDroplet - Next Steps</title>
            <link>http://example.com/2018/06/21/2018-06-21-bluedroplet/</link>
            <category>RPG</category>
            <pubDate>Thu, 21 Jun 2018 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;For those of you who don’t know yet: &lt;a href=&#34;https://bitbucket.org/m1hael/bluedroplet&#34;&gt;BlueDroplet&lt;/a&gt; 
is a native REST framework for all ILE languages on IBM i. BlueDroplet consists of multiple 
ILE modules which are bound to an ILE program. &lt;a href=&#34;https://bitbucket.org/m1hael/bluedroplet/wiki/Application_Structure&#34;&gt;Here&lt;/a&gt; 
is an overview of the structure of such a program.&lt;/p&gt;
&lt;p&gt;BlueDroplet is a work-in-progress. Some rudimentary things already work but other 
required things for a &lt;em&gt;“complete”&lt;/em&gt; REST microservice still doesn’t work, f. e. 
PUT and POST requests.&lt;/p&gt;
&lt;p&gt;Though if the service is only about providing data and not taking data as input 
you are ready to go with BlueDroplet.&lt;/p&gt;
&lt;p&gt;Is BlueDroplet production-ready? No. But it is an already useful framework none-the-less.
You can take it to get your feet wet and your hands dirty with developing your first
REST microservice on IBM i without spending a dime. You also don’t have to get some
trial license and don’t have to get and install a licensed program. Just download
the source and compile it.&lt;/p&gt;
&lt;p&gt;With BlueDroplet you can learn what it takes to build such a service.
And what is most important is to realize what works for you and your environment 
and what doesn’t. F. e. how &lt;em&gt;“big”&lt;/em&gt; should a model be? Or do you need more 
specific&amp;#x2F;smaller data models which may provide you with more flexibility. If the model
is too small the client may need to make many requests to the server before having
anything useful to display to the user. &lt;/p&gt;
&lt;p&gt;Smaller models may also mean more microservices. This requires more attention in the
administration.&lt;/p&gt;
&lt;p&gt;You also learn how to organize your code so that you can use it for the REST
service and also use it for your 5250 programs. Same code base. Just wrap it up
in service programs.&lt;/p&gt;
&lt;p&gt;There are many articles and blogs about REST services. Take a broader view
and explore the internet for information about REST. A good source of information 
for me has been &lt;a href=&#34;https://dzone.com/&#34;&gt;DZone&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And why now such an article about BlueDropet? Yes, for some time now the framework
was able to successfully process GET requests. But now it also supports processing
the URL path and query parameters. This is essential in communicating with 
modern web frameworks because many use &lt;a href=&#34;https://en.wikipedia.org/wiki/JSONP&#34;&gt;JSONP&lt;/a&gt;. 
To provide the data as JSONP the REST service needs to inspect the query parameters 
and use the callback parameter in the request response.&lt;/p&gt;
&lt;p&gt;F. e. the URL of a HTTP request with such a callback parameter may look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost:8484/customer/123?callback=angular.callbacks._0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can get the callback query parameter value with this code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;callbackId = droplet_http_getQueryValue(httpMessage : &amp;#39;callback&amp;#39;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The REST service now builds the return data and wraps the callback parameter
value around it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;angular.callbacks._0(&amp;#123;&amp;#39;id&amp;#39;:123, &amp;#39;name&amp;#39;:&amp;#39;Michael Meier&amp;#39;&amp;#125;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also query the path elements like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;customerId = %int(droplet_http_getPathElement(httpMessage : 1));
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;So what are the next steps?&lt;/h3&gt;

&lt;p&gt;As we have now support for getting the path elements and query parameter values
I can pick up development of the show case for BlueDroplet again, see project
&lt;a href=&#34;https://bitbucket.org/m1hael/storemgt&#34;&gt;Store Management&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So get your hands dirty and take some REST. ;-)&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2018/06/10/2018-06-10-llist-release-1.4.0/</guid>
            <title>Linked List Release 1.4.0</title>
            <link>http://example.com/2018/06/10/2018-06-10-llist-release-1.4.0/</link>
            <category>RPG</category>
            <pubDate>Sun, 10 Jun 2018 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;The &lt;a href=&#34;https://bitbucket.org/m1hael/llist&#34;&gt;Linked List&lt;/a&gt; service program has 
been a very stable service program with a stable API.&lt;/p&gt;
&lt;p&gt;The only thing that was a little bit clumsy was the sort function. The API
design of the function offered great flexibility. You could implement your 
own sorting with your own sort implementation. The base sort algorithm used
is the &lt;a href=&#34;https://en.wikipedia.org/wiki/Insertion_sort&#34;&gt;insertion sort&lt;/a&gt;. But 
the API offered the possiblity to use any other sort algorithm (if implemented).&lt;/p&gt;
&lt;p&gt;That was all nice and good. But the more simple use case got more complicated:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Sorting the list in a user defined way (using the base sort algorithm).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example if you add some data structure to the list and you want to 
sort the list on a subfield of the data structure. In the previous version
you would copy the whole existing sort procedure and change the part where
the values are compared. I consider this copy &amp;amp; paste development style a
really bad style and it leads to many errors.&lt;/p&gt;
&lt;p&gt;The new API makes it a lot easier to sort the list entries by just writing
a compare procedure.&lt;/p&gt;
&lt;p&gt;The prototype for such a procedure looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dcl-pr compare int(10) extproc(*dclcase);
  value1 pointer const;
  length1 int(10) const;
  value2 pointer const;
  length2 int(10) const;
end-pr;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result of the compare procedure is inspired by the way most implementations
handle this (C, Java, …).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0 means the values are equal (and the order of the list probably won’t change)&lt;/li&gt;
&lt;li&gt;less than 0 means that the first value of the compare function should be higher
in the list than the second value&lt;/li&gt;
&lt;li&gt;greater than 0 means that the second value should be higher in the list than the 
first value&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This makes comparing numbers fairly easy. For example the comparision of two 
integers.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc compareIntegers export;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-pi *n int(10);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    value1 pointer const;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    length1 int(10) const;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    value2 pointer const;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    length2 int(10) const;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  end-pi;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s x1 int(10) based(value1);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s x2 int(10) based(value2);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  return x1 - x2;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;But in the current implementations the pointer to the values can also be &lt;em&gt;*null&lt;/em&gt;.
So we must handle &lt;em&gt;*null&lt;/em&gt; values in our compare procedure which bloats it a little
bit.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc compareIntegers export;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-pi *n int(10);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    value1 pointer const;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    length1 int(10) const;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    value2 pointer const;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    length2 int(10) const;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  end-pi;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s x1 int(10) based(value1);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s x2 int(10) based(value2);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  if (value1 = *null and value2 = *null);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    return 0;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  elseif (value1 = *null);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    return 1;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  elseif (value2 = *null);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    return -1;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  else;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    return x1 - x2;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  endif;  &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;And if you would like to have a descending order than just flip the expression to&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return x2 - x1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another change is the way escape messages are sent (or more precisely where they are sent to).
Up until now they were sent to the boundary of the current program. This is not always very 
practicle and has now been changed to the be sent to the current call stack entry.&lt;/p&gt;
&lt;p&gt;The API change can be viewed in the new release and the documentation has been updated on
the &lt;a href=&#34;https://iledocs.rpgnextgen.com/&#34;&gt;ILEDocs&lt;/a&gt; website.&lt;/p&gt;
&lt;p&gt;Happy sorting!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2018/05/29/2018-05-29-iledocs-growing-again/</guid>
            <title>ILEDocs Database is growing again!</title>
            <link>http://example.com/2018/05/29/2018-05-29-iledocs-growing-again/</link>
            <category>ILEDocs</category>
            <category>RPG</category>
            <pubDate>Tue, 29 May 2018 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;ILEDocs has some fresh additions, namely the API documentation of the projects
&lt;a href=&#34;https://www.rpgnextgen.com/index.php?content=json&#34;&gt;JSON Utilities&lt;/a&gt;, 
&lt;a href=&#34;https://bitbucket.org/m1hael/llist&#34;&gt;Linked List&lt;/a&gt; and 
&lt;a href=&#34;http://rpgnextgen.com/index.php?content=lmap&#34;&gt;Linked Map&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We have almost all of the projects on board which are currently on the old 
documentation site at &lt;a href=&#34;https://iledocs.sourceforge.net/docs/&#34;&gt;Sourceforge.net&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you have an open source library and would like to add it to the ILEDocs
documentation site just drop me a mail and I will help you with it.&lt;/p&gt;
&lt;p&gt;Happy documenting!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2018/04/04/2018-04-04-jdbc-batch/</guid>
            <title>JDBCR4 and Batch Updates and Inserts</title>
            <link>http://example.com/2018/04/04/2018-04-04-jdbc-batch/</link>
            <category>Java</category>
            <pubDate>Wed, 04 Apr 2018 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;I had to work with a remote Oracle database lately and the choice was made to use 
Scott&amp;apos;s wrapper around the Java JDBC API.&lt;/p&gt;
&lt;p&gt;The solution worked. Everything is fine. Is it? No it isn&amp;apos;t.&lt;/p&gt;
&lt;p&gt;I had to insert some thousand records into the Oracle database. What JDBCR4 
supports is something like&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;statement = JDBC_PrepStmt(connection : &amp;#x27;INSERT INTO ...&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;for i = 1 to numberRecords;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  JDBC_setString(statement : 1 : data.name);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  JDBC_ExecPrepUpd(statement);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;endfor;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The data is processed record by record. That would be the same as using embedded
SQL to fetch a set of data but fetching the data row by row instead of using a 
&lt;a href=&#34;http://wiki.rpgnextgen.com/doku.php?id=rpg:multiple_row_fetch&#34;&gt;multi row fetch&lt;/a&gt; 
to get a whole block of records with one call.&lt;/p&gt;
&lt;p&gt;In Java you can do the same by batching SQL statements. You need some more prototypes
for that.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-c JDBC_STATEMENT_SUCCESS_NO_INFO -2;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-pr PreparedStatement_addBatch extproc(*java : &amp;#x27;java.sql.PreparedStatement&amp;#x27; : &amp;#x27;addBatch&amp;#x27;) end-pr;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-pr PreparedStatement_executeBatch int(10) dim(999)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;           extproc(*java : &amp;#x27;java.sql.PreparedStatement&amp;#x27; : &amp;#x27;executeBatch&amp;#x27;) end-pr;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Now the workflow looks like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;statement = JDBC_PrepStmt(connection : &amp;#x27;INSERT INTO ...&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;for i = 1 to numberRecords;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  JDBC_setString(statement : 1 : data.name);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  PreparedStatement_addBatch(statement);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;endfor;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;resultData = PreparedStatement_executeBatch(statement);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;If this really speeds up execution depends on the JDBC driver (and probably the database).&lt;/p&gt;
&lt;p&gt;Happy batching!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2018/03/26/2018-03-26-iledocs-growing/</guid>
            <title>ILEDocs Database is growing!</title>
            <link>http://example.com/2018/03/26/2018-03-26-iledocs-growing/</link>
            <category>ILEDocs</category>
            <category>RPG</category>
            <pubDate>Mon, 26 Mar 2018 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;Slowly but steadily the &lt;a href=&#34;https://bitbucket.org/m1hael/iledocs&#34;&gt;ILEDocs project&lt;/a&gt; 
is growing. Today with the new version of the 
&lt;a href=&#34;https://bitbucket.org/m1hael/message&#34;&gt;MESSAGE&lt;/a&gt; service program the API 
documentation has been uploaded to the ILEDocs database, making the API publicly 
available without having to search through any source codes.&lt;/p&gt;
&lt;p&gt;What does that mean for the ILE developers? You can find the MESSAGE API at the 
&lt;a href=&#34;https://iledocs.rpgnextgen.com/&#34;&gt;central place for documentation&lt;/a&gt; about open 
source libraries for IBM i.&lt;/p&gt;
&lt;p&gt;If you have an open source library and would like to add it to the ILEDocs
documentation site just drop me a mail and I will help you with it.&lt;/p&gt;
&lt;p&gt;Happy documenting!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2018/03/21/2018-03-21-libxlsxwriter/</guid>
            <title>libxlsxwriter - Creating spreadsheets natively</title>
            <link>http://example.com/2018/03/21/2018-03-21-libxlsxwriter/</link>
            <category>RPG</category>
            <pubDate>Wed, 21 Mar 2018 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;A short while ago I stumbled over the C library &lt;a href=&#34;https://github.com/jmcnamara/libxlsxwriter&#34;&gt;libxlsxwriter&lt;/a&gt;
from &lt;a href=&#34;https://www.linkedin.com/in/jmcnamara13/&#34;&gt;John McNamara&lt;/a&gt; on &lt;a href=&#34;https://github.com/&#34;&gt;github&lt;/a&gt;.
It is a C library for creating XLSX documents. More specifically it creates files in the 
&lt;a href=&#34;http://docs.oasis-open.org/office/v1.2/cs01/OpenDocument-v1.2-cs01.html&#34;&gt;Open Document spreadsheet format&lt;/a&gt;.
Which can be used by Microsoft Excel but also by OpenOffice and every application which supports the 
&lt;a href=&#34;http://opendocumentformat.org/&#34;&gt;Open Document&lt;/a&gt; standard.&lt;/p&gt;
&lt;p&gt;Technically the file is a ZIP archive which contains various XML files and may
contain some embedded resources like images.&lt;/p&gt;
&lt;p&gt;This library makes it so easy to create such a document.&lt;/p&gt;
&lt;p&gt;This whole project would not have interested me so much if it wasn’t for the
very strict ANSI C standard which has been used and the very clean API and
overall design and implementation. It also has very minimal requirements and
dependencies.&lt;/p&gt;
&lt;p&gt;The only third party dependency which is not shipped with the library is 
&lt;a href=&#34;https://zlib.net/&#34;&gt;zlib&lt;/a&gt; which can be compiled on the platform very nicely. 
zlib even had an upgrade to a nicer compile script. Thanks for making things easier.&lt;/p&gt;
&lt;p&gt;The other dependency (minizip) can also be compiled to an ILE C module.&lt;/p&gt;
&lt;p&gt;And thanks to the great coding the whole libxlsxwriter project can be compiled
to ILE C modules and bound to a service program. This means that now every
ILE programming language is able to create spreadsheets. &amp;#x3D;)
&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;The character encoding was a little problematic because everything is written
with the default locale which results in most cases in files with an EBCDIC CCSID. 
I have extend the code of the library to convert the data to UTF-8.&lt;/p&gt;
&lt;p&gt;Also the formatting of floating point values was a little problematic as Excel
expects the values to have a point as a decimal point. But many locales have a
comma as a decimal point sign. To overcome this problem the environment variable
&lt;code&gt;LC_NUMERIC&lt;/code&gt; can be set to an English locale.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ADDENVVAR ENVVAR(LC_NUMERIC) VALUE(&amp;#39;/QSYS.LIB/EN_US.LOCALE&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: The &lt;code&gt;DECFMT&lt;/code&gt; job value does not change the decimal point sign in this case
as the function &lt;em&gt;snprintf()&lt;/em&gt; does not take the job value &lt;code&gt;DECFMT&lt;/code&gt; into account
but the environment variable &lt;code&gt;LC_NUMERIC&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The RPG prototypes for C functions can be found in the include directory of the project.&lt;/p&gt;
&lt;p&gt;Creating a spreadsheet looks like this:
&lt;br/&gt;&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;/include &amp;#x27;xlsxwriter_h.rpgle&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-proc main;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s workbook pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  dcl-s worksheet pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  workbook = workbook_new(&amp;#x27;/home/schmidtm/test1.xlsx&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  worksheet = workbook_add_worksheet(workbook : &amp;#x27;Testsheet 1&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  worksheet_write_number(worksheet : 0 : 0 : 358 : *null);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  worksheet_write_number(worksheet : 1 : 0 : 35,8 : *null);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  worksheet_write_string(worksheet : 0 : 1 : &amp;#x27;Hello World!&amp;#x27; : *null);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  worksheet_write_string(worksheet : 1 : 1 : &amp;#x27;Umlaute: äöüÄÖÜ and ß&amp;#x27; : *null);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  workbook_close(workbook);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-proc;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;br/&gt;
The library can be installed by entering QShell (qsh) and using the Makefile.ibm script like this

&lt;pre&gt;&lt;code&gt;make -f Makefile.ibm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This compiles all necessary modules and binds them to a service program. You should adjust the 
values at the top of the Makefile.ibm to your environment.&lt;/p&gt;
&lt;p&gt;On systems configured with EBCDIC 37 this should work out-of-the-box. On other systems you may need
to convert the source file &lt;em&gt;packager.c&lt;/em&gt; to your CCSID as there are characters in the code which 
will be used in filenames (specifically brackets [ and ] ) and these may get trashed in the compile
step.&lt;/p&gt;
&lt;p&gt;The library has many features. I plan to add the prototypes step by step to the project.
Currently I added only the rudimentary features to the RPG prototypes.&lt;/p&gt;
&lt;p&gt;Images are currently also not supported in my fork of the project (but are supported in the
original code on github).&lt;/p&gt;
&lt;p&gt;The project is currently hosted on &lt;a href=&#34;https://bitbucket.org/m1hael/libxlsxwriter&#34;&gt;Bitbucket&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy spreadsheeting!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2018/03/07/2018-03-07-rdi-hover/</guid>
            <title>RDi Hover Support</title>
            <link>http://example.com/2018/03/07/2018-03-07-rdi-hover/</link>
            <category>RPG</category>
            <category>RDi</category>
            <pubDate>Wed, 07 Mar 2018 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;At the moment I have access to an RDi installation and use it on a daily
basis (besides using MiWorkplace &amp;#x3D;) ).&lt;/p&gt;
&lt;p&gt;Some people have written about the new hover support for RDi which is quite
nice (and MiWorkplace has also a nice hover support to some extent) but I 
haven’t read or heard about a very nice feature of it:&lt;/p&gt;
&lt;p style=&#34;text-align: center; font-weight: bold;&#34;&gt;Procedure documentation!&lt;/p&gt;

&lt;p&gt;Now you can put your procedure documentation before your prototype and have it
displayed in the hover info window.&lt;/p&gt;
&lt;p&gt;Example code:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;///&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// Write Number&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// Writes a number into the specified cell in the worksheet.&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;//&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// @param worksheet&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// @param row&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// @param column&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// @param number&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// @param format&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;///&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-pr worksheet_write_number int(10) extproc(&amp;#x27;worksheet_write_number&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  worksheet pointer value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  row int(10) value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  column int(10) value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  number float(8) value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  format pointer value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-pr;   &lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;br/&gt;

&lt;p&gt;The hover info for this prototoype looks like this:  &lt;/p&gt;
&lt;br/&gt;

&lt;p style=&#34;text-align: center;&#34;&gt;&lt;img src=&#34;http://rpgnextgen.com/images/rdi-hoversupport.png&#34; alt=&#34;RDi hover support&#34;/&gt;&lt;/p&gt;  
   
&lt;br/&gt;

&lt;p&gt;The information is not formatted but perhaps that will be implemented in the 
future (*hint* RFE ;-) ). It would also be nice if it would support the 
&lt;a href=&#34;https://bitbucket.org/m1hael/iledocs-page-builder&#34;&gt;ILEDocs&lt;/a&gt; tags but as IBM 
has no interest in such things support for such will probably never come.&lt;/p&gt;
&lt;p&gt;Perhaps this feature will add a little more to the poor motivation of writing
documentation. Because now you have a benefit of the documentation immediately
right at your fingertips without wading through some office documents. Just
hover with the mouse over the procedure name.&lt;/p&gt;
&lt;p&gt;Happy hovering and documenting!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2018/02/12/2018-02-12-unix-timestamp/</guid>
            <title>Measuring Time Spent</title>
            <link>http://example.com/2018/02/12/2018-02-12-unix-timestamp/</link>
            <category>RPG</category>
            <pubDate>Mon, 12 Feb 2018 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;Sometimes you have a block of code and just want to know how long it took the
machine to execute it.&lt;/p&gt;
&lt;p&gt;The Java developers have some really cool metric libraries like the 
&lt;a href=&#34;https://dropwizard.io/&#34;&gt;Dropwizard&lt;/a&gt; subproject &lt;a href=&#34;http://metrics.dropwizard.io/&#34;&gt;Metrics&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But for the quick and dirty case there is always&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;System.currentTimeMillis();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the RPG developer there are some built-in functions which can be used for
this, but nothing is as straightforward as the Java solution.&lt;/p&gt;
&lt;p&gt;But if we tap into the powers of ILE and use some C functions in RPG we can
come very close to the Java solution.&lt;/p&gt;
&lt;p&gt;The C function &lt;code&gt;time()&lt;/code&gt; returns the &lt;a href=&#34;https://en.wikipedia.org/wiki/Unix_time&#34;&gt;Unix timestamp&lt;/a&gt; 
in an integer. The prototype looks like this.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-pr time int(10) extproc(&amp;#x27;time&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  returnValue pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-pr;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The function also takes a parameter for output but we don’t need this and
can pass &lt;code&gt;*NULL&lt;/code&gt; for it.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-s NULL pointer;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dcl-s start int(10);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;start = time(NULL);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dsply %trimr(&amp;#x27;Time spent: &amp;#x27; + %char(time(NULL) - start));&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Note: The resolution of &lt;code&gt;time()&lt;/code&gt; is seconds. If milliseconds are needed
another function should be used.&lt;/p&gt;
&lt;p&gt;Happy measuring!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2018/01/27/2018-01-27-codenotes/</guid>
            <title>Project CodeNotes</title>
            <link>http://example.com/2018/01/27/2018-01-27-codenotes/</link>
            <category>CodeNotes</category>
            <pubDate>Sat, 27 Jan 2018 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;At the time I still developed the RPG Next Gen Editor I have received some requests regarding 
offline support. It was not as much about editing the code but more about being able to view 
the code (perhaps for some reviewing and later refactoring).&lt;/p&gt;
&lt;p&gt;About 10 years forward I am picking up the topic again. There still isn’t much support for 
offline viewing (though there some nice web tools for reviewing code online). But much has 
changed in the IT world with everyone and everything going mobile. So I am going mobile with 
this project too.&lt;/p&gt;
&lt;p&gt;The goal of the project is an Android app with full code review capabilities. Including &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;getting the code from various sources&lt;/li&gt;
&lt;li&gt;viewing the code&lt;/li&gt;
&lt;li&gt;taking notes in context to whole code &amp;#x2F; section of the code &lt;/li&gt;
&lt;li&gt;share notes &lt;/li&gt;
&lt;li&gt;create tickets from notes in Bitbucket, GitHub, GitLab, Taiga.io, Trac, Redmine …&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The whole project is not just an idea but already has some code behind it. There is a beta version
to download from the website. It already has some functionality. You can download code from a Git 
repository (I have tested Bitbucket and SourceForge ). Files from a file archive (like a zip or tar
archive file) can also be fetched. But I really want to have IBM i support as a first class citizen
in this project. You can browse the QSYS filesystem and the IFS and download code to your Android device.&lt;/p&gt;
&lt;p&gt;And the price for it …. nothing. Just &lt;a href=&#34;http://codenotes.rpgnextgen.com/download/codenotes-1.0.0-beta.apk&#34;&gt;download&lt;/a&gt; 
it from the &lt;a href=&#34;https://codenotes.rpgnextgen.com/&#34;&gt;website&lt;/a&gt;. If you want to show me your appreciation 
then send me a &lt;a href=&#34;mailto:mihael@rpgnextgen.com&#34;&gt;mail&lt;/a&gt; and&amp;#x2F;or make a donation on 
&lt;a href=&#34;https://www.rpgnextgen.com/&#34;&gt;rpgnextgen.com&lt;/a&gt; (top right corner on the web site).&lt;/p&gt;
&lt;p&gt;For everybody who needs another feature which is not covered by the current state of the project:&lt;/p&gt;
&lt;p style=&#34;text-align: center; font-weight: bold;&#34;&gt;Good news! You can even contribute to the project!&lt;/p&gt;

&lt;p&gt;It is &lt;strong&gt;open source&lt;/strong&gt; after all. Take the source work on it and don’t forget to contribute back to the project.&lt;/p&gt;
&lt;p&gt;So … what do you think about it?&lt;/p&gt;
&lt;p&gt;Happy reviewing!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2017/10/28/2017-10-28-modular-programming/</guid>
            <title>Modular Programming</title>
            <link>http://example.com/2017/10/28/2017-10-28-modular-programming/</link>
            <category>Design</category>
            <pubDate>Sat, 28 Oct 2017 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;blockquote&gt;
&lt;p&gt;Modular programming is a software design technique that emphasizes separating the
functionality of a program into independent, interchangeable modules, such that
each contains everything necessary to execute only one aspect of the desired
functionality.
— from &lt;a href=&#34;https://en.wikipedia.org/wiki/Modular_programming&#34;&gt;Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I think that is a good explanation and a design technique which should be used 
(probably) always and every time though on different scales.&lt;/p&gt;
&lt;h2 id=&#34;Globals&#34;&gt;&lt;a href=&#34;#Globals&#34; class=&#34;headerlink&#34; title=&#34;Globals&#34;&gt;&lt;/a&gt;Globals&lt;/h2&gt;&lt;p&gt;Each time we use global variables it probably goes against this software design 
technique and we try to minimize the use of global variables. I say “minimize” 
because in RPG it is not always possible, f. e. data areas have to be defined 
globally.&lt;/p&gt;
&lt;p&gt;And everybody seem to agree to this concept on this scale because now we don’t 
have to scan all 20000 lines of code if a certain variable is used but only the 
100 lines of the procedure. Everyone can see the benefit of it. Impact analysis 
is made in seconds (or minutes) instead of hours and we don’t need any expensive 
tools for doing it.&lt;/p&gt;
&lt;h2 id=&#34;Modular-at-System-Level&#34;&gt;&lt;a href=&#34;#Modular-at-System-Level&#34; class=&#34;headerlink&#34; title=&#34;Modular at System Level&#34;&gt;&lt;/a&gt;Modular at System Level&lt;/h2&gt;&lt;p&gt;But for some reason most RPG developers find it ok that a program should access 
any table regardless of the domain (order, master data, …). It should read and 
write data from&amp;#x2F;to it no matter what failing to see the resemblence to global 
variables. The table is the global variable in the system everyone has access 
to from everywhere. So now when we need to make a change to some table what is 
the scope of the impact analysis we have to do?&lt;/p&gt;
&lt;p style=&#34;text-align: center; font-weight: bold;&#34;&gt;THE WHOLE SYSTEM!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Could we have minimized the scope of it:&lt;/em&gt; Definitely!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;How?&lt;/em&gt; Offer access to the data via some API. But by offering some API I don’t 
mean to just add a database layer (serviceprogram&amp;#x2F;procedure) between your program 
and the table&amp;#x2F;data without adding any value to it. Just by wrapping your table 
access into another layer you have gained nothing. Any change behind the scenes 
of the API should not automatically lead to a change of the API consumer. If it 
does then you should think about what you have gained by using the API at all.&lt;/p&gt;
&lt;p&gt;Now any change to the table should only require an analysis of the program code 
of the corresponding domain and &lt;strong&gt;not the whole system&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In some cases the scope of the analysis can even be minimized to a single service 
program which makes it even easier.&lt;/p&gt;
&lt;h2 id=&#34;What-are-you-talking-about&#34;&gt;&lt;a href=&#34;#What-are-you-talking-about&#34; class=&#34;headerlink&#34; title=&#34;What are you talking about?&#34;&gt;&lt;/a&gt;What are you talking about?&lt;/h2&gt;&lt;p&gt;Many programmers don’t realize this because they have written the system themselves 
or lived in the system long enough to know it. But for everybody new to the system 
it is really hard to make even the tiniest change. Those systems are really hard 
and costly to maintain because everybody need to know &lt;strong&gt;the whole system&lt;/strong&gt;.&lt;/p&gt;
&lt;p style=&#34;text-align: center;&#34;&gt;The database should NOT be your API!&lt;/p&gt;

&lt;h2 id=&#34;Your-situation&#34;&gt;&lt;a href=&#34;#Your-situation&#34; class=&#34;headerlink&#34; title=&#34;Your situation?&#34;&gt;&lt;/a&gt;Your situation?&lt;/h2&gt;&lt;p&gt;Just ask yourself what do you need to know&amp;#x2F;analyse if you want to make a change? 
One procedure? One program? Code of one domain? The whole system?&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2017/10/10/2017-10-10-sql-clob/</guid>
            <title>Processing XML Data</title>
            <link>http://example.com/2017/10/10/2017-10-10-sql-clob/</link>
            <category>XML</category>
            <category>SQL</category>
            <category>CLOB</category>
            <pubDate>Tue, 10 Oct 2017 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;For some time now I had the idea to port a REST service of mine from Java to 
RPG. Mostly just out of curiosity because the REST service was running well 
enough. The REST service was about serving the geo coordinates of the boundaries 
of postalcode areas.&lt;/p&gt;
&lt;h2 id=&#34;Import&#34;&gt;&lt;a href=&#34;#Import&#34; class=&#34;headerlink&#34; title=&#34;Import&#34;&gt;&lt;/a&gt;Import&lt;/h2&gt;&lt;p&gt;The data source for these coordinates is the web site &lt;a href=&#34;http://suche-postleitzahl.org/&#34;&gt;suche-postleitzahl.org&lt;/a&gt;. 
They generously supply us with the latest geographic data of Germany. Thanks 
guys. Keep it going &amp;#x3D;) .&lt;/p&gt;
&lt;p&gt;The data is in the KML format which is an XML based format. So we can process 
the file with native RPG tools, in our case the opcode XML-SAX. Boundaries are 
made up of multiple coordinates. The coordinates for one boundary are located 
in the KML file as one long string of characters. The string can have a length 
of over 75000 characters. And lately (probably since IBM i 6.1) we can declare 
variables big enough to hold the content in RPG. But what about storing such a 
big character string in the database. The &lt;em&gt;CHAR&lt;/em&gt; data type has a max. size of 
32k characters (plus some more but that doesn’t matter here). But like so many 
other databases the DB 2 for i also supports the data type &lt;em&gt;CLOB&lt;/em&gt; (Character 
Large Object). So this should work out of the box with embedded SQL.&lt;/p&gt;
&lt;p&gt;But like so often … it won’t work out of the box. &lt;em&gt;CHAR&lt;/em&gt; and &lt;em&gt;CLOB&lt;/em&gt; are two 
fundamentally different data types and are not compatible in embedded SQL. The 
following code looks nice and straight forward but doesn’t work.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-s coords char(100000);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;EXEC SQL INSERT INTO postalcodes(country, postalcode, city, coords)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;         VALUES(&amp;#x27;DE&amp;#x27;, :postalcode, :city, :coords);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Since V5R4 RPG supports working with LOBs with the RPG data type &lt;em&gt;SQLTYPE&lt;/em&gt;. At 
first this didn’t seem to work out in this case at all because most tutorials 
and information about RPG and CLOBs were written when it came out with V5R4. 
But at that time RPG still had the limit for &lt;em&gt;CHAR&lt;/em&gt; fields of 65k and this was 
also pointed out in the articles. So this wouldn’t help because the coordinate 
data is bigger than 65k. But that was at V5R4. Since then much has happened and 
now you can declare &lt;em&gt;CHAR&lt;/em&gt; variables with up to 16m characters.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-s coords sqltype(clob : 100000);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;EXEC SQL INSERT INTO postalcodes(country, postalcode, city, coords)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;         VALUES(&amp;#x27;DE&amp;#x27;, :postalcode, :city, :coords);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;h2 id=&#34;REST-Service&#34;&gt;&lt;a href=&#34;#REST-Service&#34; class=&#34;headerlink&#34; title=&#34;REST Service&#34;&gt;&lt;/a&gt;REST Service&lt;/h2&gt;&lt;p&gt;The REST service part of it would be implemented by using the ILE REST framework 
&lt;a href=&#34;https://bitbucket.org/m1hael/bluedroplet&#34;&gt;BlueDroplet&lt;/a&gt;. But some features are 
not implemented yet in the framework and so this part has to wait a little bit 
longer.&lt;/p&gt;
&lt;h2 id=&#34;Source-Code&#34;&gt;&lt;a href=&#34;#Source-Code&#34; class=&#34;headerlink&#34; title=&#34;Source Code&#34;&gt;&lt;/a&gt;Source Code&lt;/h2&gt;&lt;p&gt;You can take a look at the code &lt;a href=&#34;https://bitbucket.org/m1hael/postalcodes&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;Notes&#34;&gt;&lt;a href=&#34;#Notes&#34; class=&#34;headerlink&#34; title=&#34;Notes&#34;&gt;&lt;/a&gt;Notes&lt;/h2&gt;&lt;p&gt;Saving the coordinates in the format from the KML file is not ideal in any way 
because now every time a boundary is requested the string of coordinates need 
to be converted into the desired format. It would be much better to convert 
them directly on the import so we would have to convert them once instead of x 
times. But that has to wait till the &lt;a href=&#34;https://bitbucket.org/m1hael/bluedroplet&#34;&gt;BlueDroplet&lt;/a&gt; 
project has proceeded further and has implemented the feature to parse the query 
string of an HTTP request. So that we can really use this data and return it not 
as JSON but as &lt;a href=&#34;https://en.wikipedia.org/wiki/JSONP&#34;&gt;JSONP&lt;/a&gt; which makes it much 
easier to access and process it in the fronted (JavaScript).&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2017/10/02/2017-02-10-bluedroplet-at-iledocs/</guid>
            <title>BlueDroplet at ILEDocs</title>
            <link>http://example.com/2017/10/02/2017-02-10-bluedroplet-at-iledocs/</link>
            <category>ILEDocs</category>
            <category>BlueDroplet</category>
            <pubDate>Mon, 02 Oct 2017 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;As there is now a functioning &lt;a href=&#34;http://iledocs.rpgnextgen.com/&#34;&gt;ILEDocs&lt;/a&gt; site 
available at rpgnextgen.com I added some real content to it.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://bitbucket.org/m1hael/bluedroplet&#34;&gt;BlueDroplet&lt;/a&gt; project is now 
in an alpha stage and can already be used for some REST services. The ILEDocs
documentation of the BlueDroplet gives you a nice and quick overview of the 
available API from BlueDroplet and helps you developing your REST services
with BlueDroplet.&lt;/p&gt;
&lt;p&gt;What do you think about the documentation. Anything missing? I would like to 
hear your thoughts and feedback.&lt;/p&gt;
&lt;p&gt;Happy browsing!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2017/08/26/2017-08-26-stomp/</guid>
            <title>Even more Integration Possibilities</title>
            <link>http://example.com/2017/08/26/2017-08-26-stomp/</link>
            <category>RPG</category>
            <category>STOMP</category>
            <pubDate>Sat, 26 Aug 2017 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;IBM &lt;strong&gt;i&lt;/strong&gt; stands for &lt;em&gt;integration&lt;/em&gt;. But it is most of the time very troublesome to connect to and integrate anything &lt;strong&gt;outside&lt;/strong&gt; the IBM family. In the open world (outside the IBM i box) there are numerous ways to integrate and communicate between systems and applications. One way to do it is to use a messaging system. There are numerous messaging systems out there (like ActiveMQ, Artemis, Apollo from Apache or RabbitMQ to name only a few). Even IBM has such a system by the name of IBM MQ (short names must be the way-to-go at the moment as it was formerly named IBM Websphere MQ and MQSeries before that. IBM has a long track record of renaming things and we all know , don’t we?! ;-) ).&lt;/p&gt;
&lt;p&gt;But for RPG developers there was no easy way to communicate with these systems. Most programming language have some libraries which can be used to communicate with these systems in an easy way. RPG as so often doesn’t have one.&lt;/p&gt;
&lt;p&gt;Most of these messaging systems support the STOMP protocol. That is a simple text protocol which can be used to talk to these systems.&lt;/p&gt;
&lt;p&gt;From the former STOMP web site (at codehaus.org):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The STOMP project is the Streaming Text Orientated Messaging Protocol site (or
the Protocol Briefly Known as TTMP and Represented by the symbol :ttmp).
STOMP provides an interoperable wire format so that any of the available Stomp
Clients can communicate with any STOMP Message Broker to provide easy and 
widespread messaging interop among languages, platforms and brokers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So putting together a client library for RPG should not be that big of a problem and after all it wasn’t (except for some stumbling blocks when it came to socket APIs).&lt;/p&gt;
&lt;p&gt;The client library is available as an ILE service program written in RPG at its &lt;a href=&#34;https://bitbucket.org/m1hael/stomp&#34;&gt;project website&lt;/a&gt; hosted on &lt;a href=&#34;https://bitbucket.org/&#34;&gt;Bitbucket&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;Sending-Data&#34;&gt;&lt;a href=&#34;#Sending-Data&#34; class=&#34;headerlink&#34; title=&#34;Sending Data&#34;&gt;&lt;/a&gt;Sending Data&lt;/h2&gt;&lt;p&gt;A common format should be chosen for the data which is sent to the messaging system. Currently JSON and XML are very common and easy to process formats.&lt;/p&gt;
&lt;p&gt;Basically the following steps are needed to send data to a messaging system:&lt;/p&gt;
&lt;p&gt;Create an instance of the STOMP client:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;client = stomp_create(&amp;#x27;mq.server.com&amp;#x27; : 61613);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Connect on the network level (socket connection):&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;stomp_open(client);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Connect on the application level (STOMP protocol):&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;stomp_command_connect(client : &amp;#x27;user&amp;#x27; : &amp;#x27;pass&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Send the data:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;stomp_command_send(client : &amp;#x27;/queue/items&amp;#x27; : &amp;#x27;&amp;#123; &amp;quot;id&amp;quot;:5500 , &amp;quot;price&amp;quot;:1.29 &amp;#125;&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Disconnect on the application level (STOMP protocol):&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;stomp_command_disconnect(client);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Disconnect on the network client (close socket connection):&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;stomp_close(client);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;Clean up and free all allocated memory:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;stomp_finalize(client);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;At the moment all data is sent as ASCII. The STOMP protocol (at least in its recent version) also supports other encodings but that is not implemented yet (contributions are welcome &amp;#x3D;) ).&lt;/p&gt;
&lt;h2 id=&#34;Documentation&#34;&gt;&lt;a href=&#34;#Documentation&#34; class=&#34;headerlink&#34; title=&#34;Documentation&#34;&gt;&lt;/a&gt;Documentation&lt;/h2&gt;&lt;p&gt;The API documentation has been created with &lt;a href=&#34;https://bitbucket.org/m1hael/iledocs&#34;&gt;ILEDocs&lt;/a&gt; and can (soon) be viewed &lt;a href=&#34;http://iledocs.rpgnextgen.com/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;Examples&#34;&gt;&lt;a href=&#34;#Examples&#34; class=&#34;headerlink&#34; title=&#34;Examples&#34;&gt;&lt;/a&gt;Examples&lt;/h2&gt;&lt;p&gt;The project repository has an &lt;code&gt;examples&lt;/code&gt; folder which contains some code on how to use the procedures.&lt;/p&gt;
&lt;p&gt;There is also a complete demo project available on &lt;a href=&#34;https://bitbucket.org/m1hael/weather&#34;&gt;Bitbucket&lt;/a&gt; which shows how to use the procedures.&lt;/p&gt;
&lt;h2 id=&#34;Help&#34;&gt;&lt;a href=&#34;#Help&#34; class=&#34;headerlink&#34; title=&#34;Help&#34;&gt;&lt;/a&gt;Help&lt;/h2&gt;&lt;p&gt;If you need any help with setting things up or on how to use the service program feel free to contact me at &lt;a href=&#34;mailto:&amp;#109;&amp;#x69;&amp;#104;&amp;#x61;&amp;#101;&amp;#x6c;&amp;#64;&amp;#x72;&amp;#x70;&amp;#103;&amp;#x6e;&amp;#101;&amp;#x78;&amp;#116;&amp;#103;&amp;#101;&amp;#x6e;&amp;#46;&amp;#x63;&amp;#111;&amp;#x6d;&#34;&gt;&amp;#109;&amp;#x69;&amp;#104;&amp;#x61;&amp;#101;&amp;#x6c;&amp;#64;&amp;#x72;&amp;#x70;&amp;#103;&amp;#x6e;&amp;#101;&amp;#x78;&amp;#116;&amp;#103;&amp;#101;&amp;#x6e;&amp;#46;&amp;#x63;&amp;#111;&amp;#x6d;&lt;/a&gt; .&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2017/08/24/2017-08-24-socket-timeout/</guid>
            <title>Hey Socket, take a break!</title>
            <link>http://example.com/2017/08/24/2017-08-24-socket-timeout/</link>
            <category>RPG</category>
            <pubDate>Thu, 24 Aug 2017 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;Ever wanted a normal (blocking) socket connection to listen for data but also
wanted to check for a condition if it should stop listening?&lt;/p&gt;
&lt;p&gt;Problem is that the default BSD 4.3 version of the socket API (which is the 
default API to bind to) does not support setting timeouts. Trying to set a 
timeout for receiving data results in&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;3021 : The value specified for the argument is not correct.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But by switching to the BSD 4.4 &amp;#x2F; Unix98) version of the socket API you can set
timeouts with the &lt;code&gt;setsockopt&lt;/code&gt; function.&lt;/p&gt;
&lt;h2 id=&#34;How-to-do-it&#34;&gt;&lt;a href=&#34;#How-to-do-it&#34; class=&#34;headerlink&#34; title=&#34;How to do it&#34;&gt;&lt;/a&gt;How to do it&lt;/h2&gt;&lt;p&gt;The timeout can be set directly after the &lt;code&gt;connect()&lt;/code&gt; call. It is set by calling
the &lt;code&gt;setsockopt()&lt;/code&gt; function with the corresponding parameters.&lt;/p&gt;
&lt;p&gt;Parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Socket descriptor&lt;/li&gt;
&lt;li&gt;SOL_SOCKET for stating that it will be a socket option&lt;/li&gt;
&lt;li&gt;SO_RCVTIMEO for changing the timeout for receiving data&lt;/li&gt;
&lt;li&gt;timeout value&lt;/li&gt;
&lt;li&gt;timeout value length&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The prototype looks Like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-pr setsockopt int(10) extproc(&amp;#x27;qso_setsockopt98&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  socket int(10) value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  level int(10) value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  option_name int(10) value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  option_value pointer value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  option_length int(10) value;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-pr;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The timeout is passed as a data structure containing seconds and microseconds
for the timeout value.&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;dcl-ds timeout_t qualified template;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  seconds int(10);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  useconds int(10);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end-ds;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;The call may look something like this:&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;if (setsockopt(socket : SOL_SOCKET: SO_RCVTIMEO : %addr(timeout) : %size(timeout)) = -1);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  errPtr = errno();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  message_sendEscapeMessageToCaller(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;Could not set socket timeout. &amp;#x27; + %char(c_err) + &amp;#x27;: &amp;#x27; + %str(strerror(c_err)));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;endif;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&#34;Binding&#34;&gt;&lt;a href=&#34;#Binding&#34; class=&#34;headerlink&#34; title=&#34;Binding&#34;&gt;&lt;/a&gt;Binding&lt;/h2&gt;&lt;p&gt;The socket functions are contained in the service programs &lt;code&gt;QSOSRV1&lt;/code&gt;. The 
contained modules and functions can be displayed with &lt;code&gt;DSPSRVPGM QSOSRV1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The BSD 4.3 and 4.4 versions of the Socket API are contained in this service
program. The BSD 4.4 version (which is also identical to the Unix98 version) 
is prefixed with &lt;em&gt;qso_&lt;/em&gt; and suffixed with &lt;em&gt;98&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;So the program can be created in the usual way and be bound to the same service
program. The names in the prototypes are important as they decide which functions
are called.&lt;/p&gt;
&lt;h2 id=&#34;Example&#34;&gt;&lt;a href=&#34;#Example&#34; class=&#34;headerlink&#34; title=&#34;Example&#34;&gt;&lt;/a&gt;Example&lt;/h2&gt;&lt;p&gt;For a working example take a look at the &lt;a href=&#34;https://bitbucket.org/m1hael/stomp&#34;&gt;STOMP client&lt;/a&gt;, 
source file &lt;em&gt;stomp.rpgle&lt;/em&gt;, procedure &lt;em&gt;stomp_open&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Have a nice break.&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2017/05/21/2017-05-21-xmlservice-rant/</guid>
            <title>Rant about XMLSERVICE</title>
            <link>http://example.com/2017/05/21/2017-05-21-xmlservice-rant/</link>
            <category>XMLSERVICE</category>
            <pubDate>Sun, 21 May 2017 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;Hi,&lt;/p&gt;
&lt;p&gt;First of all I want to make it clear that I have nothing against XMLSERVICE. I 
think it is a nice tool though I never had to work with it due to my current 
job and thus have no real life experience with it.&lt;/p&gt;
&lt;p&gt;And this ranting is not really about the product itself but more about the 
marketing and what the marketing does to the IBM i community.&lt;/p&gt;
&lt;p&gt;So what bugs me most is the marketing of “Now you can offer REST services to 
the world!”. If it would have been marketed not as REST services but as web 
services there wouldn’t be anything to rant about because there is no real&amp;#x2F;strict 
definition of what a web service really is (though many people have some 
strong expectations when they hear about web services).&lt;/p&gt;
&lt;p&gt;The W3C defines a web service generally as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A web service is a software system designed to support interoperable machine-to-machine interaction over a network.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And I want to throw in that I am no REST service expert by any means but even 
myself with my intermediate REST service skills I see many flaws in the way it 
is marketed.&lt;/p&gt;
&lt;p&gt;First of all REST is all about the &lt;em&gt;resource&lt;/em&gt; and the resource is stated in the 
URL. So if I want some information about a book the URL may look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://ibmi/book/1234
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;where 1234 is the id of the book.&lt;/p&gt;
&lt;p&gt;With XMLSERVICE you call the &lt;strong&gt;global&lt;/strong&gt; XMLSERVICE program. You don’t call the 
resource you want to know about.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://ibmi:58700/cgi-bin/xmlcgi.pgm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another thing is that the HTTP method is directly linked to the action you want 
to execute on the resource. GET retrieves some information and DELETE deletes 
the information on the server. With XMLSERVICE there is no such mapping to HTTP 
methods. AFAIK there are only GET and POST supported.&lt;/p&gt;
&lt;p&gt;What about the media type? Normally you would want to support at least JSON and 
XML and perhaps even some other format. But the media type does not only state 
the expected return format of the information. It can contain much more, f. e. 
the API version.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;application/vnd.company.book-v3+xml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;HTTP Status Codes … what about the returned status codes. I would expect a 404 
if the book is not in the database of the server. What about authentifiation and 
authorization status codes? Or if the user cannot update the resource because it 
is blocked by some other transaction? I would want the user to be informed about 
that. Just throwing some 500 or not even at all (actually I don’t know what 
XMLSERVICE does in these cases) is not good. Fact is that the program which handles 
the resoure should set the HTTP status code and not XMLSERVICE.&lt;/p&gt;
&lt;p&gt;There are probably many other details that are not correct when calling XMLSERVICE 
a REST service provider. But that is not really the point here. In calling XMLSERVICE 
a REST provider or a framework&amp;#x2F;toolkit which can provide REST services (which it 
obviously doesn’t) you make it look non-professional. For someone really deep into 
REST services this more or less looks like a joke (which it definitely isn’t). But 
developers from other platform are laughing about this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A: “Hey, you know … IBM i now can provide their data as REST services. *giggle*“&lt;/p&gt;
&lt;p&gt;B: “Yeah … *giggle* … at least I can get the data with some HTTP call.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So calling XMLSERVICE a framework for creating REST services is also adding to how 
other developers (from other platforms) see the IBM i platform (and i don’t mean in 
a positive way).&lt;/p&gt;
&lt;p&gt;The only technical argument I have with XMLSERVICE: Actually I don’t understand the 
rational about the design concept of XMLSERVICE. In (almost) every language there is 
the trend to make small libraries which do exactly one thing and with as little 
dependencies as possible to build modular applications. But XMLSERVICE is the exact 
opposite.&lt;/p&gt;
&lt;p&gt;If you can’t be mainstream stop pretending to be mainstream when you obviously are not.&lt;/p&gt;
&lt;p&gt;And again I would like to mention that I find it great to have XMLSERVICE because it 
adds to the small list of tools and libraries available to IBM developers whereas 
developers on other platforms can’t even count the number of tools&amp;#x2F;libraries available 
to them.&lt;/p&gt;
&lt;p&gt;End of my rant.&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2017/05/14/2017-05-14-ci/</guid>
            <title>Continuous Integration on IBM i</title>
            <link>http://example.com/2017/05/14/2017-05-14-ci/</link>
            <category>CI</category>
            <pubDate>Sun, 14 May 2017 00:00:00 +0200</pubDate>
            <description><![CDATA[ &lt;p&gt;Hi,&lt;/p&gt;
&lt;p&gt;continuous integration has been a topic on any other platform for some years 
now. But on IBM i I never heard that term in any discussion.&lt;/p&gt;
&lt;p&gt;There is a myriad of open source tooling support on any platform but not for IBM i …
or rather there is tooling for anything but for ILE on IBM i.&lt;/p&gt;
&lt;p&gt;The central piece of software for CI is a build server which runs the build 
jobs but can also execute other tasks like unit and integration test and can
also be used to automatically deploy the build to a test or integration
environment. Jenkins is such a build server.&lt;/p&gt;
&lt;p&gt;I have written an article about the results of my adventures with Jenkins and
some RPG code. You can find it in my &lt;a href=&#34;http://wiki.rpgnextgen.com/doku.php?id=ibm_i_and_continuous_integration&#34;&gt;wiki&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy integrating!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2017/02/19/2017-02-19-lists/</guid>
            <title>Arraylist vs Linked List</title>
            <link>http://example.com/2017/02/19/2017-02-19-lists/</link>
            <category>RPG</category>
            <pubDate>Sun, 19 Feb 2017 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;Hi,&lt;/p&gt;
&lt;p&gt;I am just moving my service program 
&lt;a href=&#34;https://github.com/OSSILE/OSSILE/tree/master/main/linkedlist&#34;&gt;Linked List&lt;/a&gt; to the OSSILE project 
and I am again wondering if people know why there are two list implementations
and when to use which implemenation or when to use a list at all.&lt;/p&gt;
&lt;p&gt;I am doing most of my development in the Java world and there lists are really
cheap. You will encounter them in Java everywhere. In the RPG world it is exactly 
the other way around. Arrays are really cheap and there is not even a list data&lt;br&gt;structure natively in RPG.&lt;/p&gt;
&lt;p&gt;Note: And by data structure I don&amp;apos;t mean a &lt;code&gt;DS&lt;/code&gt; or &lt;code&gt;dcl-ds&lt;/code&gt; declared
variable but something more general as explained on 
&lt;a href=&#34;https://en.wikipedia.org/wiki/Data_structure&#34;&gt;this page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So using a list is not as cheap as using an array from a performance point of 
view. But it definitely has its pros. If you don&amp;apos;t know the size of the
result you want to return from a procedure you are at a loss when using an
array because no matter how hard you try you cannot do it correctly. The number
of returned elements will almost never match the size of your array. The array
will either be too big or may be even too small (which would be rather fatal).&lt;/p&gt;
&lt;p&gt;And this was my main motiviation of implementing a list. Because with a list
the number of elements doesn&amp;apos;t matter. The list starts with the number
of elements you declare (probably none) and grows as you add elements to it.
Depending on the available API it may also have some very convenient procedures
like rotate all elements in the list, remove a range of elements, split a
string of characters by one separator to a list, last index of element, swap
elements, … .&lt;/p&gt;
&lt;h2 id=&#34;Arraylist-vs-Linked-List&#34;&gt;&lt;a href=&#34;#Arraylist-vs-Linked-List&#34; class=&#34;headerlink&#34; title=&#34;Arraylist vs Linked List&#34;&gt;&lt;/a&gt;Arraylist vs Linked List&lt;/h2&gt;&lt;p&gt;The main difference of these lists is the way the data is stored and accessed.&lt;/p&gt;
&lt;h3 id=&#34;Arraylist&#34;&gt;&lt;a href=&#34;#Arraylist&#34; class=&#34;headerlink&#34; title=&#34;Arraylist&#34;&gt;&lt;/a&gt;Arraylist&lt;/h3&gt;&lt;p&gt;The Arraylist service program uses one big block of data to store the pointers
to each element. By doing that it can really quickly access elements by index.
Adding elements to the end of the list is performing normally but adding 
to the beginning of the list is a bad idea performance wise because all pointers
to the other elements must be shifted down by one position. Also adding elements
could result in the need to expand this block of memory where the pointers are
stored. This operation may have a bigger impact on performance. Iterating through
the nodes is really easy as it can jump to the next node by just one arthimetic
expression.&lt;/p&gt;
&lt;h3 id=&#34;Linked-List&#34;&gt;&lt;a href=&#34;#Linked-List&#34; class=&#34;headerlink&#34; title=&#34;Linked List&#34;&gt;&lt;/a&gt;Linked List&lt;/h3&gt;&lt;p&gt;The Linked List service program is implemented as a two-way linked list where
each node has a pointer to the previous node and to the next node. Because of
this inserting a node at any place takes the same time no matter how large the
list grows. Iterating through the list is also very cheap as you go from node
to node. But accessing the list by index is not a good idea because the list has
to start at the beginning of the list and then count till it reaches the node
at the specified index. &lt;/p&gt;
&lt;h2 id=&#34;Not-good-at&#34;&gt;&lt;a href=&#34;#Not-good-at&#34; class=&#34;headerlink&#34; title=&#34;Not good at&#34;&gt;&lt;/a&gt;Not good at&lt;/h2&gt;&lt;p&gt;Both lists are not good at doing one thing: &lt;em&gt;locating an element by value&lt;/em&gt;. So
if you want to check if a value is already in the list then you can do it.
Both lists support it by the &lt;code&gt;contains&lt;/code&gt; procedure but both lists have to start
from the beginning of the list and check every element if it has the same value
as the passed one. For this kind of operation these two lists are the wrong
data structure or rather the wrong implementation to use. You would need something
like a sorted list where all entries are sorted at any time. Or use a tree data
structure. &lt;/p&gt;
&lt;h2 id=&#34;Wrap-it-up&#34;&gt;&lt;a href=&#34;#Wrap-it-up&#34; class=&#34;headerlink&#34; title=&#34;Wrap it up&#34;&gt;&lt;/a&gt;Wrap it up&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;If you know the size of the content you should use an array.&lt;/li&gt;
&lt;li&gt;If you don&amp;apos;t know the size of an array use a list.&lt;/li&gt;
&lt;li&gt;If you need to access the list by index use the Arraylist.&lt;/li&gt;
&lt;li&gt;If you have many insert and remove operations on the list use the Linked List.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope that gave you a good idea about when to use a list and which implementation
to use.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2017/02/10/2017-02-10-bluedroplet-libl/</guid>
            <title>BlueDroplet and the library list</title>
            <link>http://example.com/2017/02/10/2017-02-10-bluedroplet-libl/</link>
            <category>BlueDroplet</category>
            <pubDate>Fri, 10 Feb 2017 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;BlueDroplet now supports changing the library list on startup. You can either
add library list entries to the user portion of the library list or replace the 
whole user portion of the library list.&lt;/p&gt;
&lt;p&gt;Happy RESTing!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">http://example.com/2017/02/09/2017-02-09%20ILEDocs-Revival/</guid>
            <title>ILEDocs Revival</title>
            <link>http://example.com/2017/02/09/2017-02-09%20ILEDocs-Revival/</link>
            <category>ILEDocs</category>
            <pubDate>Thu, 09 Feb 2017 00:00:00 +0100</pubDate>
            <description><![CDATA[ &lt;p&gt;Hi,&lt;/p&gt;
&lt;p&gt;creating documentation has always been a major PITA in software development. 
Tools try to ease the process of creating good documentation. There are 
different kind of documentations (mostly different by the scope and&amp;#x2F;or detail 
and&amp;#x2F;or perspective) and not every documentation tool is good for every kind 
of documentation.&lt;/p&gt;
&lt;p&gt;API documentation is one kind of documentation. It is rather easy to create in 
most programming languages as there are many good toolchains like 
&lt;a href=&#34;http://www.oracle.com/technetwork/java/javase/documentation/index-jsp-135444.html&#34;&gt;Javadoc&lt;/a&gt; 
or &lt;a href=&#34;http://www.stack.nl/~dimitri/doxygen/&#34;&gt;doxygen&lt;/a&gt;. Sadly for development on 
IBM i you will not find a good toolchain or documentation support on our beloved 
platform for your own development (I am not talking about the IBM i Information 
Center).&lt;/p&gt;
&lt;p&gt;I tried to tackle this problem once with a not so pretty solution. Especially 
installation and maintenance was not very well thought out. Editor support was 
also not very good or even non-existent.&lt;/p&gt;
&lt;p&gt;Now some years later and with new technologies appearing I have a feeling that 
I can tackle this problem much better and easier than before. I revived the 
ILEDocs project with a fresh approach using &lt;a href=&#34;https://dropwizard.github.io/&#34;&gt;Dropwizard&lt;/a&gt; 
for the backend and &lt;a href=&#34;https://angularjs.org/&#34;&gt;AngularJS&lt;/a&gt; for the frontend. A 
&lt;a href=&#34;https://www.mongodb.org/&#34;&gt;MongoDB&lt;/a&gt; database will store the documentation.&lt;/p&gt;
&lt;p&gt;Updating the documenation is now tooling independant as we can work with a REST 
service and just need to upload a JSON document.&lt;/p&gt;
&lt;p&gt;The MiWorkplace editor will get some extra features like querying the 
documentation database and updating the documentation directly from the 
editor.&lt;/p&gt;
&lt;p&gt;The documentation could also be updated from some workflow like a TurnOver form 
run utilizing the old ILEDocs RPG and CL parser, converting the parse result 
to JSON and sending it with the HTTP Client to the ILEDocs REST service. Though
this is not implemented.&lt;/p&gt;
&lt;p&gt;The project is not finished but already quite usable. It is open source and 
available under the Apache License 2.0.&lt;/p&gt;
&lt;p&gt;You can take a look at it at &lt;a href=&#34;https://bitbucket.org/m1hael/iledocs&#34;&gt;bitbucket.org&lt;/a&gt;. 
The wiki at the bitbucket project site contains some useful information on how 
to setup your own documentation server.&lt;/p&gt;
&lt;p&gt;One of my goal is also to create a universal documentation server for all open 
source IBM i projects. Please contact me if you would like to help.&lt;/p&gt;
&lt;p&gt;A prototype is also already online with some demo content at 
&lt;a href=&#34;http://iledocs.rpgnextgen.com/&#34;&gt;iledocs.rpgnextgen.com&lt;/a&gt;. Feel free 
to give it a try.&lt;/p&gt;
&lt;p&gt;I would like to hear your thoughts and feedback.&lt;/p&gt;
&lt;p&gt;Happy documenting!&lt;/p&gt;
&lt;p&gt;Mihael&lt;/p&gt;
 ]]></description>
        </item>
    </channel>
</rss>
